🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@shadowob/cli

Package Overview
Dependencies
Maintainers
1
Versions
73
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@shadowob/cli - npm Package Compare versions

Comparing version
1.1.4
to
1.1.5
+266
dist/chunk-E364BDQO.js
// src/utils/client.ts
import { ShadowClient, ShadowSocket } from "@shadowob/sdk";
// src/config/manager.ts
import { existsSync } from "fs";
import { mkdir, readFile, writeFile } from "fs/promises";
import { homedir } from "os";
import { dirname, join } from "path";
var DEFAULT_CONFIG_DIR = join(homedir(), ".shadowob");
var _DEFAULT_CONFIG_FILE = join(DEFAULT_CONFIG_DIR, "shadowob.config.json");
var ConfigManager = class {
config = null;
configFile;
constructor(configDir) {
const dir = configDir ?? DEFAULT_CONFIG_DIR;
this.configFile = join(dir, "shadowob.config.json");
}
async load() {
if (this.config) return this.config;
if (!existsSync(this.configFile)) {
this.config = { profiles: {} };
return this.config;
}
try {
const content = await readFile(this.configFile, "utf-8");
this.config = JSON.parse(content);
return this.config;
} catch {
this.config = { profiles: {} };
return this.config;
}
}
async save() {
if (!this.config) return;
await mkdir(dirname(this.configFile), { recursive: true });
await writeFile(this.configFile, JSON.stringify(this.config, null, 2));
}
async getProfile(name) {
const config = await this.load();
const profileName = name ?? config.currentProfile;
if (!profileName) return null;
return config.profiles[profileName] ?? null;
}
async getCurrentProfileName() {
const config = await this.load();
return config.currentProfile ?? null;
}
async setProfile(name, profile) {
const config = await this.load();
config.profiles[name] = profile;
await this.save();
}
async deleteProfile(name) {
const config = await this.load();
if (!config.profiles[name]) return false;
delete config.profiles[name];
if (config.currentProfile === name) {
delete config.currentProfile;
}
await this.save();
return true;
}
async switchProfile(name) {
const config = await this.load();
if (!config.profiles[name]) return false;
config.currentProfile = name;
await this.save();
return true;
}
async listProfiles() {
const config = await this.load();
return Object.keys(config.profiles);
}
getConfigPath() {
return this.configFile;
}
async validate() {
const result = {
valid: true,
errors: [],
warnings: [],
profileResults: {}
};
if (!existsSync(this.configFile)) {
result.valid = false;
result.errors.push("Config file does not exist");
return result;
}
let config;
try {
const content = await readFile(this.configFile, "utf-8");
config = JSON.parse(content);
} catch (error) {
result.valid = false;
result.errors.push(`Invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
return result;
}
if (!config.profiles || typeof config.profiles !== "object") {
result.valid = false;
result.errors.push('Missing or invalid "profiles" field');
return result;
}
if (config.currentProfile) {
if (!config.profiles[config.currentProfile]) {
result.valid = false;
result.errors.push(`Current profile "${config.currentProfile}" does not exist`);
}
} else {
result.warnings.push("No current profile set");
}
for (const [name, profile] of Object.entries(config.profiles)) {
const profileResult = { valid: true };
if (!profile.serverUrl) {
profileResult.valid = false;
result.errors.push(`Profile "${name}" missing serverUrl`);
} else {
try {
new URL(profile.serverUrl);
} catch {
profileResult.valid = false;
result.errors.push(`Profile "${name}" has invalid serverUrl: ${profile.serverUrl}`);
}
}
if (!profile.token) {
profileResult.valid = false;
result.errors.push(`Profile "${name}" missing token`);
} else if (!profile.token.includes(".")) {
result.warnings.push(`Profile "${name}" token does not look like a JWT`);
}
result.profileResults[name] = profileResult;
if (!profileResult.valid) {
result.valid = false;
}
}
return result;
}
async fix() {
const changes = [];
const config = await this.load();
for (const [name, profile] of Object.entries(config.profiles)) {
if (!profile.serverUrl || !profile.token) {
delete config.profiles[name];
changes.push(`Removed invalid profile "${name}"`);
}
}
if (config.currentProfile && !config.profiles[config.currentProfile]) {
const remainingProfiles = Object.keys(config.profiles);
if (remainingProfiles.length > 0) {
config.currentProfile = remainingProfiles[0];
changes.push(`Reset current profile to "${config.currentProfile}"`);
} else {
delete config.currentProfile;
changes.push("Removed invalid current profile reference");
}
}
if (!config.profiles) {
config.profiles = {};
changes.push("Created empty profiles object");
}
await this.save();
return { fixed: changes.length > 0, changes };
}
};
var configManager = new ConfigManager();
// src/utils/client.ts
async function getConfig(profile) {
const config = await configManager.getProfile(profile);
if (!config) {
throw new Error(
profile ? `Profile "${profile}" not found. Run: shadowob auth login --profile ${profile}` : "Not authenticated. Run: shadowob auth login"
);
}
return config;
}
async function getClient(profile) {
const config = await getConfig(profile);
return new ShadowClient(config.serverUrl, config.token);
}
async function getClientWithToken(token, profile) {
const config = await getConfig(profile);
return new ShadowClient(config.serverUrl, token);
}
async function getSocket(profile) {
const config = await getConfig(profile);
return new ShadowSocket({ serverUrl: config.serverUrl, token: config.token });
}
function formatError(error) {
if (error instanceof Error) {
return error.message;
}
return String(error);
}
function parseLimit(value, defaultValue = 50, maxValue = 100) {
if (!value) return defaultValue;
const parsed = parseInt(value, 10);
if (Number.isNaN(parsed) || parsed < 1) return defaultValue;
return Math.min(parsed, maxValue);
}
function parsePrice(value) {
const parsed = parseFloat(value);
if (Number.isNaN(parsed) || parsed < 0) {
throw new Error("Price must be a non-negative number");
}
return parsed;
}
function parseIntOrThrow(value, fieldName) {
const parsed = parseInt(value, 10);
if (Number.isNaN(parsed)) {
throw new Error(`${fieldName} must be a valid integer`);
}
return parsed;
}
function parsePositiveInt(value, fieldName) {
const parsed = parseIntOrThrow(value, fieldName);
if (parsed < 1) {
throw new Error(`${fieldName} must be a positive integer`);
}
return parsed;
}
function parseNonNegativeInt(value, fieldName) {
const parsed = parseIntOrThrow(value, fieldName);
if (parsed < 0) {
throw new Error(`${fieldName} must be a non-negative integer`);
}
return parsed;
}
function parseBoolean(value) {
if (value === void 0) return void 0;
if (value === "true" || value === "1") return true;
if (value === "false" || value === "0") return false;
return void 0;
}
function requireOption(value, name) {
if (value === void 0 || value === null || value === "") {
throw new Error(`Missing required option: --${name}`);
}
return value;
}
async function handleCommand(fn, options, outputFn, errorFn) {
try {
const result = await fn();
outputFn(result, options.json);
process.exit(0);
} catch (error) {
const message = formatError(error);
errorFn(message, options.json);
process.exit(1);
}
}
export {
configManager,
getClient,
getClientWithToken,
getSocket,
formatError,
parseLimit,
parsePrice,
parseIntOrThrow,
parsePositiveInt,
parseNonNegativeInt,
parseBoolean,
requireOption,
handleCommand
};
import {
formatError,
getClient,
getClientWithToken,
getSocket,
handleCommand,
parseBoolean,
parseIntOrThrow,
parseLimit,
parseNonNegativeInt,
parsePositiveInt,
parsePrice,
requireOption
} from "./chunk-E364BDQO.js";
export {
formatError,
getClient,
getClientWithToken,
getSocket,
handleCommand,
parseBoolean,
parseIntOrThrow,
parseLimit,
parseNonNegativeInt,
parsePositiveInt,
parsePrice,
requireOption
};
+2
-2
{
"name": "@shadowob/cli",
"version": "1.1.4",
"version": "1.1.5",
"description": "Shadow CLI — command-line interface for Shadow servers",

@@ -16,3 +16,3 @@ "license": "MIT",

"chalk": "^5.4.1",
"@shadowob/sdk": "1.1.4"
"@shadowob/sdk": "1.1.5"
},

@@ -19,0 +19,0 @@ "devDependencies": {

// src/utils/client.ts
import { ShadowClient, ShadowSocket } from "@shadowob/sdk";
// src/config/manager.ts
import { existsSync } from "fs";
import { mkdir, readFile, writeFile } from "fs/promises";
import { homedir } from "os";
import { dirname, join } from "path";
var DEFAULT_CONFIG_DIR = join(homedir(), ".shadowob");
var _DEFAULT_CONFIG_FILE = join(DEFAULT_CONFIG_DIR, "shadowob.config.json");
var ConfigManager = class {
config = null;
configFile;
constructor(configDir) {
const dir = configDir ?? DEFAULT_CONFIG_DIR;
this.configFile = join(dir, "shadowob.config.json");
}
async load() {
if (this.config) return this.config;
if (!existsSync(this.configFile)) {
this.config = { profiles: {} };
return this.config;
}
try {
const content = await readFile(this.configFile, "utf-8");
this.config = JSON.parse(content);
return this.config;
} catch {
this.config = { profiles: {} };
return this.config;
}
}
async save() {
if (!this.config) return;
await mkdir(dirname(this.configFile), { recursive: true });
await writeFile(this.configFile, JSON.stringify(this.config, null, 2));
}
async getProfile(name) {
const config = await this.load();
const profileName = name ?? config.currentProfile;
if (!profileName) return null;
return config.profiles[profileName] ?? null;
}
async getCurrentProfileName() {
const config = await this.load();
return config.currentProfile ?? null;
}
async setProfile(name, profile) {
const config = await this.load();
config.profiles[name] = profile;
await this.save();
}
async deleteProfile(name) {
const config = await this.load();
if (!config.profiles[name]) return false;
delete config.profiles[name];
if (config.currentProfile === name) {
delete config.currentProfile;
}
await this.save();
return true;
}
async switchProfile(name) {
const config = await this.load();
if (!config.profiles[name]) return false;
config.currentProfile = name;
await this.save();
return true;
}
async listProfiles() {
const config = await this.load();
return Object.keys(config.profiles);
}
getConfigPath() {
return this.configFile;
}
async validate() {
const result = {
valid: true,
errors: [],
warnings: [],
profileResults: {}
};
if (!existsSync(this.configFile)) {
result.valid = false;
result.errors.push("Config file does not exist");
return result;
}
let config;
try {
const content = await readFile(this.configFile, "utf-8");
config = JSON.parse(content);
} catch (error) {
result.valid = false;
result.errors.push(`Invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
return result;
}
if (!config.profiles || typeof config.profiles !== "object") {
result.valid = false;
result.errors.push('Missing or invalid "profiles" field');
return result;
}
if (config.currentProfile) {
if (!config.profiles[config.currentProfile]) {
result.valid = false;
result.errors.push(`Current profile "${config.currentProfile}" does not exist`);
}
} else {
result.warnings.push("No current profile set");
}
for (const [name, profile] of Object.entries(config.profiles)) {
const profileResult = { valid: true };
if (!profile.serverUrl) {
profileResult.valid = false;
result.errors.push(`Profile "${name}" missing serverUrl`);
} else {
try {
new URL(profile.serverUrl);
} catch {
profileResult.valid = false;
result.errors.push(`Profile "${name}" has invalid serverUrl: ${profile.serverUrl}`);
}
}
if (!profile.token) {
profileResult.valid = false;
result.errors.push(`Profile "${name}" missing token`);
} else if (!profile.token.includes(".")) {
result.warnings.push(`Profile "${name}" token does not look like a JWT`);
}
result.profileResults[name] = profileResult;
if (!profileResult.valid) {
result.valid = false;
}
}
return result;
}
async fix() {
const changes = [];
const config = await this.load();
for (const [name, profile] of Object.entries(config.profiles)) {
if (!profile.serverUrl || !profile.token) {
delete config.profiles[name];
changes.push(`Removed invalid profile "${name}"`);
}
}
if (config.currentProfile && !config.profiles[config.currentProfile]) {
const remainingProfiles = Object.keys(config.profiles);
if (remainingProfiles.length > 0) {
config.currentProfile = remainingProfiles[0];
changes.push(`Reset current profile to "${config.currentProfile}"`);
} else {
delete config.currentProfile;
changes.push("Removed invalid current profile reference");
}
}
if (!config.profiles) {
config.profiles = {};
changes.push("Created empty profiles object");
}
await this.save();
return { fixed: changes.length > 0, changes };
}
};
var configManager = new ConfigManager();
// src/utils/client.ts
async function getConfig(profile) {
const config = await configManager.getProfile(profile);
if (!config) {
throw new Error(
profile ? `Profile "${profile}" not found. Run: shadowob auth login --profile ${profile}` : "Not authenticated. Run: shadowob auth login"
);
}
return config;
}
async function getClient(profile) {
const config = await getConfig(profile);
return new ShadowClient(config.serverUrl, config.token);
}
async function getSocket(profile) {
const config = await getConfig(profile);
return new ShadowSocket({ serverUrl: config.serverUrl, token: config.token });
}
function formatError(error) {
if (error instanceof Error) {
return error.message;
}
return String(error);
}
function parseLimit(value, defaultValue = 50, maxValue = 100) {
if (!value) return defaultValue;
const parsed = parseInt(value, 10);
if (Number.isNaN(parsed) || parsed < 1) return defaultValue;
return Math.min(parsed, maxValue);
}
function parsePrice(value) {
const parsed = parseFloat(value);
if (Number.isNaN(parsed) || parsed < 0) {
throw new Error("Price must be a non-negative number");
}
return parsed;
}
function parseIntOrThrow(value, fieldName) {
const parsed = parseInt(value, 10);
if (Number.isNaN(parsed)) {
throw new Error(`${fieldName} must be a valid integer`);
}
return parsed;
}
function parsePositiveInt(value, fieldName) {
const parsed = parseIntOrThrow(value, fieldName);
if (parsed < 1) {
throw new Error(`${fieldName} must be a positive integer`);
}
return parsed;
}
function parseNonNegativeInt(value, fieldName) {
const parsed = parseIntOrThrow(value, fieldName);
if (parsed < 0) {
throw new Error(`${fieldName} must be a non-negative integer`);
}
return parsed;
}
function parseBoolean(value) {
if (value === void 0) return void 0;
if (value === "true" || value === "1") return true;
if (value === "false" || value === "0") return false;
return void 0;
}
function requireOption(value, name) {
if (value === void 0 || value === null || value === "") {
throw new Error(`Missing required option: --${name}`);
}
return value;
}
async function handleCommand(fn, options, outputFn, errorFn) {
try {
const result = await fn();
outputFn(result, options.json);
process.exit(0);
} catch (error) {
const message = formatError(error);
errorFn(message, options.json);
process.exit(1);
}
}
export {
configManager,
getClient,
getSocket,
formatError,
parseLimit,
parsePrice,
parseIntOrThrow,
parsePositiveInt,
parseNonNegativeInt,
parseBoolean,
requireOption,
handleCommand
};
import {
formatError,
getClient,
getSocket,
handleCommand,
parseBoolean,
parseIntOrThrow,
parseLimit,
parseNonNegativeInt,
parsePositiveInt,
parsePrice,
requireOption
} from "./chunk-T3BKMB7N.js";
export {
formatError,
getClient,
getSocket,
handleCommand,
parseBoolean,
parseIntOrThrow,
parseLimit,
parseNonNegativeInt,
parsePositiveInt,
parsePrice,
requireOption
};

Sorry, the diff of this file is too big to display