@scheduler-systems/gal-run 0.0.263 → 0.0.266

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.cjs +720 -76
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -3970,7 +3970,7 @@ var cliVersion, defaultApiUrl, BUILD_CONSTANTS, constants_default;
3970
3970
  var init_constants = __esm({
3971
3971
  "src/constants.ts"() {
3972
3972
  "use strict";
3973
- cliVersion = true ? "0.0.263" : "0.0.0-dev";
3973
+ cliVersion = true ? "0.0.266" : "0.0.0-dev";
3974
3974
  defaultApiUrl = true ? "https://api.gal.run" : "http://localhost:3000";
3975
3975
  BUILD_CONSTANTS = Object.freeze([cliVersion, defaultApiUrl]);
3976
3976
  constants_default = BUILD_CONSTANTS;
@@ -4812,7 +4812,7 @@ function detectEnvironment() {
4812
4812
  return "dev";
4813
4813
  }
4814
4814
  try {
4815
- const version = true ? "0.0.263" : void 0;
4815
+ const version = true ? "0.0.266" : void 0;
4816
4816
  if (version && version.includes("-local")) {
4817
4817
  return "dev";
4818
4818
  }
@@ -5181,7 +5181,7 @@ function getId() {
5181
5181
  }
5182
5182
  function getCliVersion() {
5183
5183
  try {
5184
- return true ? "0.0.263" : "0.0.0-dev";
5184
+ return true ? "0.0.266" : "0.0.0-dev";
5185
5185
  } catch {
5186
5186
  return "0.0.0-dev";
5187
5187
  }
@@ -7731,7 +7731,7 @@ var init_feature_flags2 = __esm({
7731
7731
  init_src();
7732
7732
  COMMAND_CATEGORIES = {
7733
7733
  // Core convenience package (always visible)
7734
- core: ["auth", "sync", "mcp", "feedback", "update", "hooks", "status", "discover", "approve", "propose", "workspace", "fetch", "join", "scan", "memory"],
7734
+ core: ["auth", "sync", "mcp", "feedback", "update", "hooks", "status", "discover", "approve", "approved-config", "propose", "workspace", "fetch", "join", "scan", "memory"],
7735
7735
  // Internal development commands (requires admin org membership or GAL_INTERNAL=true)
7736
7736
  internal: [
7737
7737
  "admin",
@@ -20802,56 +20802,6 @@ var init_agent = __esm({
20802
20802
  }
20803
20803
  });
20804
20804
 
20805
- // src/utils/capability-checker.ts
20806
- async function checkOrgCapability(authRepo, orgName, requiredCapability, commandName) {
20807
- try {
20808
- const context = await authRepo.getUserContext();
20809
- const org = context.orgs.find((o) => o.name === orgName);
20810
- if (!org) {
20811
- console.error(source_default.red(`You don't have access to organization "${orgName}"`));
20812
- console.log(source_default.dim("\nAvailable organizations:"));
20813
- if (context.orgs.length === 0) {
20814
- console.log(source_default.dim(" (none)"));
20815
- console.log(source_default.dim("\nMake sure you have joined the organization on GitHub."));
20816
- } else {
20817
- context.orgs.forEach((o) => {
20818
- console.log(source_default.dim(` - ${o.name} (${o.githubRole})`));
20819
- });
20820
- }
20821
- return false;
20822
- }
20823
- if (!org.capabilities[requiredCapability]) {
20824
- const capabilityMessages = {
20825
- canManageApprovedConfig: `manage approved configs for ${orgName}`,
20826
- canRunDiscovery: `run discovery scans for ${orgName}`,
20827
- canManageTeam: `manage team members for ${orgName}`,
20828
- canSyncConfig: `sync configs from ${orgName}`
20829
- };
20830
- console.error(source_default.red(`You don't have permission to ${capabilityMessages[requiredCapability]}`));
20831
- console.log(source_default.dim(`
20832
- Your role in ${orgName}: ${source_default.white(org.githubRole)}`));
20833
- console.log(source_default.dim("Required role: admin"));
20834
- console.log(source_default.dim("\nContact your organization admin to grant you admin access on GitHub."));
20835
- return false;
20836
- }
20837
- return true;
20838
- } catch (error2) {
20839
- if ((error2 instanceof Error ? error2.message : "").includes("401") || (error2 instanceof Error ? error2.message : "").includes("Unauthorized")) {
20840
- console.error(source_default.red("Authentication failed. Please log in again:"));
20841
- console.log(source_default.dim(" gal auth login"));
20842
- } else {
20843
- console.error(source_default.red(`Failed to check permissions: ${error2 instanceof Error ? error2.message : String(error2)}`));
20844
- }
20845
- return false;
20846
- }
20847
- }
20848
- var init_capability_checker = __esm({
20849
- "src/utils/capability-checker.ts"() {
20850
- "use strict";
20851
- init_source();
20852
- }
20853
- });
20854
-
20855
20805
  // src/services/CoreServiceProvider.ts
20856
20806
  var CoreServiceProvider;
20857
20807
  var init_CoreServiceProvider = __esm({
@@ -21075,6 +21025,378 @@ var init_CoreServiceProvider = __esm({
21075
21025
  }
21076
21026
  });
21077
21027
 
21028
+ // src/commands/approved-config.ts
21029
+ function makeProvider(config) {
21030
+ return new CoreServiceProvider({
21031
+ apiUrl: config.apiUrl || process.env.GAL_API_URL || "",
21032
+ authToken: config.authToken
21033
+ });
21034
+ }
21035
+ function resolveOrg(options, config) {
21036
+ const org = options.org || config.defaultOrg;
21037
+ if (!org) {
21038
+ console.error(source_default.red("No organization specified."));
21039
+ console.error(source_default.dim("Use --org <name> or run `gal workspace use <name>` to set a default."));
21040
+ process.exit(1);
21041
+ }
21042
+ return org;
21043
+ }
21044
+ function printApprovedConfig(config, org) {
21045
+ console.log();
21046
+ console.log(source_default.bold(`Approved config for "${org}" (${config.platform})`));
21047
+ console.log(source_default.dim(` Version: ${config.version || "unknown"}`));
21048
+ console.log(source_default.dim(` Approved at: ${config.approvedAt || "unknown"}`));
21049
+ console.log(source_default.dim(` Approved by: ${config.approvedBy || "unknown"}`));
21050
+ console.log(source_default.dim(` Hash: ${config.hash || "unknown"}`));
21051
+ if (config.policyName) {
21052
+ console.log(source_default.dim(` Policy: ${config.policyName}`));
21053
+ }
21054
+ console.log();
21055
+ const commandCount = config.commandCount ?? config.commands?.length ?? 0;
21056
+ const subagentCount = config.subagentCount ?? config.subagents?.length ?? 0;
21057
+ const skillCount = config.skillCount ?? config.skills?.length ?? 0;
21058
+ const ruleCount = config.ruleCount ?? config.rules?.length ?? 0;
21059
+ console.log(source_default.bold(" Contents:"));
21060
+ console.log(` Commands: ${commandCount}`);
21061
+ console.log(` Agents: ${subagentCount}`);
21062
+ console.log(` Skills: ${skillCount}`);
21063
+ console.log(` Rules: ${ruleCount}`);
21064
+ console.log(` Instructions: ${config.instructions ? "yes" : "no"}`);
21065
+ console.log(` Settings: ${config.settings ? "yes" : "no"}`);
21066
+ if (commandCount > 0 && config.commands) {
21067
+ console.log();
21068
+ console.log(source_default.bold(" Commands:"));
21069
+ config.commands.forEach((c) => console.log(` /${c.name}`));
21070
+ }
21071
+ if (subagentCount > 0 && config.subagents) {
21072
+ console.log();
21073
+ console.log(source_default.bold(" Agents:"));
21074
+ config.subagents.forEach((s) => console.log(` ${s.name}`));
21075
+ }
21076
+ if (skillCount > 0 && config.skills) {
21077
+ console.log();
21078
+ console.log(source_default.bold(" Skills:"));
21079
+ config.skills.forEach((s) => console.log(` ${s.name}`));
21080
+ }
21081
+ if (ruleCount > 0 && config.rules) {
21082
+ console.log();
21083
+ console.log(source_default.bold(" Rules:"));
21084
+ config.rules.forEach((r) => console.log(` ${r.name}`));
21085
+ }
21086
+ console.log();
21087
+ }
21088
+ function createApprovedConfigCommand() {
21089
+ const command = new Command("approved-config");
21090
+ command.description("View and manage the org-approved AI agent configuration");
21091
+ command.command("show").description("Show the current approved configuration for an organization").option("-o, --org <name>", "Organization name (default: from config)").option("-p, --platform <platform>", "Platform (default: claude)", "claude").option("--json", "Output as JSON").action(async (options) => {
21092
+ const config = ConfigManager.load();
21093
+ if (!config.authToken) {
21094
+ console.error(source_default.red("Not authenticated. Run: gal auth login"));
21095
+ process.exit(1);
21096
+ }
21097
+ const org = resolveOrg(options, config);
21098
+ const provider = makeProvider(config);
21099
+ const configRepo = provider.getConfigRepository();
21100
+ const spinner = options.json ? null : ora(`Fetching approved config for "${org}"...`).start();
21101
+ try {
21102
+ const approved = await configRepo.getApprovedConfig(org, options.platform);
21103
+ spinner?.stop();
21104
+ if (!approved) {
21105
+ if (options.json) {
21106
+ console.log(JSON.stringify({ organization: org, platform: options.platform, approved: null }));
21107
+ } else {
21108
+ console.log(source_default.yellow(`No approved config found for "${org}" (${options.platform}).`));
21109
+ console.log(source_default.dim("Run `gal approve` to set one."));
21110
+ }
21111
+ return;
21112
+ }
21113
+ if (options.json) {
21114
+ console.log(JSON.stringify({ organization: org, approved }));
21115
+ } else {
21116
+ printApprovedConfig(approved, org);
21117
+ }
21118
+ } catch (error2) {
21119
+ spinner?.stop();
21120
+ const msg = error2 instanceof Error ? error2.message : String(error2);
21121
+ if (options.json) {
21122
+ console.log(JSON.stringify({ error: msg }));
21123
+ } else {
21124
+ console.error(source_default.red(`Error: ${msg}`));
21125
+ }
21126
+ process.exit(1);
21127
+ }
21128
+ });
21129
+ command.command("diff").description("Show diff between local configs and the approved config").option("-o, --org <name>", "Organization name (default: from config)").option("-p, --platform <platform>", "Platform (default: claude)", "claude").option("-d, --dir <path>", "Local directory to compare against (default: cwd)").option("--json", "Output as JSON").action(async (options) => {
21130
+ const config = ConfigManager.load();
21131
+ if (!config.authToken) {
21132
+ console.error(source_default.red("Not authenticated. Run: gal auth login"));
21133
+ process.exit(1);
21134
+ }
21135
+ const org = resolveOrg(options, config);
21136
+ const provider = makeProvider(config);
21137
+ const configRepo = provider.getConfigRepository();
21138
+ const spinner = options.json ? null : ora(`Fetching approved config for "${org}"...`).start();
21139
+ try {
21140
+ let findMdFiles2 = function(dir) {
21141
+ if (!existsSync41(dir)) return [];
21142
+ const entries = readdirSync16(dir, { withFileTypes: true });
21143
+ const files = [];
21144
+ for (const entry of entries) {
21145
+ const fullPath = join45(dir, entry.name);
21146
+ if (entry.isDirectory()) files.push(...findMdFiles2(fullPath));
21147
+ else if (entry.name.endsWith(".md")) files.push(fullPath);
21148
+ }
21149
+ return files;
21150
+ };
21151
+ var findMdFiles = findMdFiles2;
21152
+ const approved = await configRepo.getApprovedConfig(org, options.platform);
21153
+ spinner?.stop();
21154
+ if (!approved) {
21155
+ if (options.json) {
21156
+ console.log(JSON.stringify({ organization: org, platform: options.platform, approved: null, diff: null }));
21157
+ } else {
21158
+ console.log(source_default.yellow(`No approved config found for "${org}" (${options.platform}).`));
21159
+ console.log(source_default.dim("Run `gal approve` to set one."));
21160
+ }
21161
+ return;
21162
+ }
21163
+ const { existsSync: existsSync41, readdirSync: readdirSync16, readFileSync: readFileSync37 } = await import("fs");
21164
+ const { join: join45, relative: relative10, basename: basename9 } = await import("path");
21165
+ const baseDir = options.dir || process.cwd();
21166
+ const commandsDir = join45(baseDir, ".claude", "commands");
21167
+ const agentsDir = join45(baseDir, ".claude", "agents");
21168
+ const localCommandNames = findMdFiles2(commandsDir).map(
21169
+ (f) => relative10(commandsDir, f).replace(/\.md$/, "").replace(/\//g, ":")
21170
+ );
21171
+ const localAgentNames = findMdFiles2(agentsDir).map(
21172
+ (f) => relative10(agentsDir, f).replace(/\.md$/, "").replace(/\//g, ":")
21173
+ );
21174
+ const approvedCommandNames = (approved.commands ?? []).map((c) => c.name);
21175
+ const approvedAgentNames = (approved.subagents ?? []).map((s) => s.name);
21176
+ const addedCommands = localCommandNames.filter((n) => !approvedCommandNames.includes(n));
21177
+ const removedCommands = approvedCommandNames.filter((n) => !localCommandNames.includes(n));
21178
+ const addedAgents = localAgentNames.filter((n) => !approvedAgentNames.includes(n));
21179
+ const removedAgents = approvedAgentNames.filter((n) => !localAgentNames.includes(n));
21180
+ if (options.json) {
21181
+ console.log(JSON.stringify({
21182
+ organization: org,
21183
+ platform: options.platform,
21184
+ diff: {
21185
+ commands: { added: addedCommands, removed: removedCommands },
21186
+ agents: { added: addedAgents, removed: removedAgents }
21187
+ }
21188
+ }));
21189
+ return;
21190
+ }
21191
+ const hasChanges = addedCommands.length || removedCommands.length || addedAgents.length || removedAgents.length;
21192
+ if (!hasChanges) {
21193
+ console.log(source_default.green(`\u2713 Local configs match approved config for "${org}" (${options.platform}).`));
21194
+ return;
21195
+ }
21196
+ console.log();
21197
+ console.log(source_default.bold(`Diff: local vs. approved config for "${org}" (${options.platform})`));
21198
+ console.log();
21199
+ if (addedCommands.length) {
21200
+ console.log(source_default.bold(" Commands only in local (would be added on `gal approve`):"));
21201
+ addedCommands.forEach((n) => console.log(source_default.green(` + /${n}`)));
21202
+ }
21203
+ if (removedCommands.length) {
21204
+ console.log(source_default.bold(" Commands only in approved (would be removed locally):"));
21205
+ removedCommands.forEach((n) => console.log(source_default.red(` - /${n}`)));
21206
+ }
21207
+ if (addedAgents.length) {
21208
+ console.log(source_default.bold(" Agents only in local:"));
21209
+ addedAgents.forEach((n) => console.log(source_default.green(` + ${n}`)));
21210
+ }
21211
+ if (removedAgents.length) {
21212
+ console.log(source_default.bold(" Agents only in approved:"));
21213
+ removedAgents.forEach((n) => console.log(source_default.red(` - ${n}`)));
21214
+ }
21215
+ console.log();
21216
+ } catch (error2) {
21217
+ spinner?.stop();
21218
+ const msg = error2 instanceof Error ? error2.message : String(error2);
21219
+ if (options.json) {
21220
+ console.log(JSON.stringify({ error: msg }));
21221
+ } else {
21222
+ console.error(source_default.red(`Error: ${msg}`));
21223
+ }
21224
+ process.exit(1);
21225
+ }
21226
+ });
21227
+ command.command("remove <type> <name>").description("Remove a specific item (command, agent, skill, rule) from the approved config").option("-o, --org <name>", "Organization name (default: from config)").option("-p, --platform <platform>", "Platform (default: claude)", "claude").option("--dry-run", "Preview removal without making changes").option("--json", "Output as JSON").action(async (type, name, options) => {
21228
+ const validTypes = ["command", "agent", "skill", "rule"];
21229
+ if (!validTypes.includes(type)) {
21230
+ const msg = `Invalid type "${type}". Valid types: ${validTypes.join(", ")}`;
21231
+ if (options.json) console.log(JSON.stringify({ error: msg }));
21232
+ else console.error(source_default.red(msg));
21233
+ process.exit(1);
21234
+ }
21235
+ const config = ConfigManager.load();
21236
+ if (!config.authToken) {
21237
+ console.error(source_default.red("Not authenticated. Run: gal auth login"));
21238
+ process.exit(1);
21239
+ }
21240
+ const org = resolveOrg(options, config);
21241
+ const provider = makeProvider(config);
21242
+ const configRepo = provider.getConfigRepository();
21243
+ if (options.dryRun) {
21244
+ if (options.json) {
21245
+ console.log(JSON.stringify({ dryRun: true, type, name, org, platform: options.platform }));
21246
+ } else {
21247
+ console.log(source_default.yellow(`Dry run: would remove ${type} "${name}" from "${org}" (${options.platform}).`));
21248
+ }
21249
+ return;
21250
+ }
21251
+ const spinner = options.json ? null : ora(`Removing ${type} "${name}"...`).start();
21252
+ try {
21253
+ const items = {};
21254
+ if (type === "command") items.commands = [name];
21255
+ else if (type === "agent") items.subagents = [name];
21256
+ else if (type === "skill") items.skills = [name];
21257
+ else if (type === "rule") items.rules = [name];
21258
+ const result = await configRepo.removeFromApprovedConfig(org, options.platform, items);
21259
+ spinner?.succeed(`Removed ${type} "${name}"`);
21260
+ if (options.json) {
21261
+ console.log(JSON.stringify({ success: true, ...result }));
21262
+ } else {
21263
+ console.log(source_default.dim(` Remaining: ${result.remaining.commands} commands, ${result.remaining.subagents} agents, ${result.remaining.skills} skills, ${result.remaining.rules} rules`));
21264
+ console.log(source_default.dim(` New hash: ${result.hash}`));
21265
+ }
21266
+ } catch (error2) {
21267
+ spinner?.stop();
21268
+ const msg = error2 instanceof Error ? error2.message : String(error2);
21269
+ if (options.json) {
21270
+ console.log(JSON.stringify({ error: msg }));
21271
+ } else {
21272
+ console.error(source_default.red(`Error: ${msg}`));
21273
+ }
21274
+ process.exit(1);
21275
+ }
21276
+ });
21277
+ command.command("clear").description("Clear the entire approved configuration for a platform").option("-o, --org <name>", "Organization name (default: from config)").option("-p, --platform <platform>", "Platform (default: claude)", "claude").option("--dry-run", "Preview clearing without making changes").option("--json", "Output as JSON").action(async (options) => {
21278
+ const config = ConfigManager.load();
21279
+ if (!config.authToken) {
21280
+ console.error(source_default.red("Not authenticated. Run: gal auth login"));
21281
+ process.exit(1);
21282
+ }
21283
+ const org = resolveOrg(options, config);
21284
+ if (options.dryRun) {
21285
+ if (options.json) {
21286
+ console.log(JSON.stringify({ dryRun: true, org, platform: options.platform }));
21287
+ } else {
21288
+ console.log(source_default.yellow(`Dry run: would clear approved config for "${org}" (${options.platform}).`));
21289
+ }
21290
+ return;
21291
+ }
21292
+ const provider = makeProvider(config);
21293
+ const configRepo = provider.getConfigRepository();
21294
+ const spinner = options.json ? null : ora(`Clearing approved config for "${org}" (${options.platform})...`).start();
21295
+ try {
21296
+ await configRepo.deleteApprovedConfig(org, options.platform);
21297
+ spinner?.succeed(`Cleared approved config for "${org}" (${options.platform})`);
21298
+ if (options.json) {
21299
+ console.log(JSON.stringify({ success: true, org, platform: options.platform }));
21300
+ }
21301
+ } catch (error2) {
21302
+ spinner?.stop();
21303
+ const msg = error2 instanceof Error ? error2.message : String(error2);
21304
+ if (options.json) {
21305
+ console.log(JSON.stringify({ error: msg }));
21306
+ } else {
21307
+ console.error(source_default.red(`Error: ${msg}`));
21308
+ }
21309
+ process.exit(1);
21310
+ }
21311
+ });
21312
+ command.command("export").description("Export the approved configuration to stdout or a file").option("-o, --org <name>", "Organization name (default: from config)").option("-p, --platform <platform>", "Platform (default: claude)", "claude").option("--format <format>", "Output format: json (default)", "json").action(async (options) => {
21313
+ const config = ConfigManager.load();
21314
+ if (!config.authToken) {
21315
+ console.error(source_default.red("Not authenticated. Run: gal auth login"));
21316
+ process.exit(1);
21317
+ }
21318
+ const org = resolveOrg(options, config);
21319
+ const provider = makeProvider(config);
21320
+ const configRepo = provider.getConfigRepository();
21321
+ const spinner = ora(`Fetching approved config for "${org}"...`).start();
21322
+ try {
21323
+ const approved = await configRepo.getApprovedConfig(org, options.platform);
21324
+ spinner?.stop();
21325
+ if (!approved) {
21326
+ console.log(JSON.stringify({ organization: org, platform: options.platform, approved: null }));
21327
+ return;
21328
+ }
21329
+ console.log(JSON.stringify({ organization: org, approved }, null, 2));
21330
+ } catch (error2) {
21331
+ spinner?.stop();
21332
+ const msg = error2 instanceof Error ? error2.message : String(error2);
21333
+ console.error(source_default.red(`Error: ${msg}`));
21334
+ process.exit(1);
21335
+ }
21336
+ });
21337
+ return command;
21338
+ }
21339
+ var init_approved_config = __esm({
21340
+ "src/commands/approved-config.ts"() {
21341
+ "use strict";
21342
+ init_esm();
21343
+ init_source();
21344
+ init_ora();
21345
+ init_config_manager();
21346
+ init_CoreServiceProvider();
21347
+ }
21348
+ });
21349
+
21350
+ // src/utils/capability-checker.ts
21351
+ async function checkOrgCapability(authRepo, orgName, requiredCapability, commandName) {
21352
+ try {
21353
+ const context = await authRepo.getUserContext();
21354
+ const org = context.orgs.find((o) => o.name === orgName);
21355
+ if (!org) {
21356
+ console.error(source_default.red(`You don't have access to organization "${orgName}"`));
21357
+ console.log(source_default.dim("\nAvailable organizations:"));
21358
+ if (context.orgs.length === 0) {
21359
+ console.log(source_default.dim(" (none)"));
21360
+ console.log(source_default.dim("\nMake sure you have joined the organization on GitHub."));
21361
+ } else {
21362
+ context.orgs.forEach((o) => {
21363
+ console.log(source_default.dim(` - ${o.name} (${o.githubRole})`));
21364
+ });
21365
+ }
21366
+ return false;
21367
+ }
21368
+ if (!org.capabilities[requiredCapability]) {
21369
+ const capabilityMessages = {
21370
+ canManageApprovedConfig: `manage approved configs for ${orgName}`,
21371
+ canRunDiscovery: `run discovery scans for ${orgName}`,
21372
+ canManageTeam: `manage team members for ${orgName}`,
21373
+ canSyncConfig: `sync configs from ${orgName}`
21374
+ };
21375
+ console.error(source_default.red(`You don't have permission to ${capabilityMessages[requiredCapability]}`));
21376
+ console.log(source_default.dim(`
21377
+ Your role in ${orgName}: ${source_default.white(org.githubRole)}`));
21378
+ console.log(source_default.dim("Required role: admin"));
21379
+ console.log(source_default.dim("\nContact your organization admin to grant you admin access on GitHub."));
21380
+ return false;
21381
+ }
21382
+ return true;
21383
+ } catch (error2) {
21384
+ if ((error2 instanceof Error ? error2.message : "").includes("401") || (error2 instanceof Error ? error2.message : "").includes("Unauthorized")) {
21385
+ console.error(source_default.red("Authentication failed. Please log in again:"));
21386
+ console.log(source_default.dim(" gal auth login"));
21387
+ } else {
21388
+ console.error(source_default.red(`Failed to check permissions: ${error2 instanceof Error ? error2.message : String(error2)}`));
21389
+ }
21390
+ return false;
21391
+ }
21392
+ }
21393
+ var init_capability_checker = __esm({
21394
+ "src/utils/capability-checker.ts"() {
21395
+ "use strict";
21396
+ init_source();
21397
+ }
21398
+ });
21399
+
21078
21400
  // ../../node_modules/.pnpm/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js
21079
21401
  var require_identity = __commonJS({
21080
21402
  "../../node_modules/.pnpm/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js"(exports2) {
@@ -29348,7 +29670,7 @@ function incrementVersion(version) {
29348
29670
  return version;
29349
29671
  }
29350
29672
  function createApproveCommand() {
29351
- const approve = new Command("approve").description("Upload local configs to org approved config").option("-o, --org <name>", "Organization name (default: from config)").option("-p, --platform <platform>", "Platform to approve for", "claude").option("-d, --dir <path>", "Directory to read configs from", process.cwd()).option("--dry-run", "Preview what would be uploaded without making changes").option("--commands-only", "Only approve commands (no agents, instructions, settings)").option("--agents-only", "Only approve agents (no commands, instructions, settings)").option("--clear", "Clear entire approved config for the platform").option("--remove <names...>", "Remove specific commands/agents by name (comma-separated)").option("--local", "Save to .gal/config.yaml locally (no auth required, no API upload)").action(async (options) => {
29673
+ const approve = new Command("approve").description("Upload local configs to org approved config").option("-o, --org <name>", "Organization name (default: from config)").option("-p, --platform <platform>", "Platform to approve for", "claude").option("-d, --dir <path>", "Directory to read configs from", process.cwd()).option("--dry-run", "Preview what would be uploaded without making changes").option("--commands-only", "Only approve commands (no agents, instructions, settings)").option("--agents-only", "Only approve agents (no commands, instructions, settings)").option("--clear", "Clear entire approved config for the platform").option("--remove <names...>", "Remove specific commands/agents by name (comma-separated)").option("--local", "Save to .gal/config.yaml locally (no auth required, no API upload)").option("--from-discovered", "Approve configs sourced from the discovered repositories pool").option("--type <type>", "Filter discovered configs by type: command, agent, skill, rule").option("--repo <repo>", "Filter discovered configs to a specific repository").option("--diff", "Show a unified diff before approving").action(async (options) => {
29352
29674
  if (options.commandsOnly && options.agentsOnly) {
29353
29675
  console.error(source_default.red("\nError: --commands-only and --agents-only are mutually exclusive."));
29354
29676
  console.log(source_default.dim("Use one or the other, or neither to approve all configs.\n"));
@@ -29362,6 +29684,67 @@ function createApproveCommand() {
29362
29684
  console.error(source_default.red("\nError: --remove cannot be combined with --commands-only or --agents-only."));
29363
29685
  process.exit(1);
29364
29686
  }
29687
+ if (options.fromDiscovered) {
29688
+ const config2 = ConfigManager.load();
29689
+ if (!config2.authToken) {
29690
+ console.error(source_default.red("Not authenticated. Run: gal auth login"));
29691
+ process.exit(1);
29692
+ }
29693
+ const orgName2 = options.org || config2.defaultOrg;
29694
+ if (!orgName2) {
29695
+ console.error(source_default.red("No organization specified. Use --org or run `gal workspace use <name>`."));
29696
+ process.exit(1);
29697
+ }
29698
+ const provider2 = new CoreServiceProvider({
29699
+ apiUrl: config2.apiUrl || process.env.GAL_API_URL || "",
29700
+ authToken: config2.authToken
29701
+ });
29702
+ const scanRepo = provider2.getScanResultRepository();
29703
+ const spinner2 = ora(`Fetching discovered configs for "${orgName2}"...`).start();
29704
+ try {
29705
+ const cache = await scanRepo.getDiscoveredConfigsCache(orgName2);
29706
+ spinner2.stop();
29707
+ let configs = cache?.configs || [];
29708
+ if (options.repo) {
29709
+ configs = configs.filter((c) => c.repo === options.repo || c.repoName === options.repo);
29710
+ }
29711
+ if (options.type) {
29712
+ configs = configs.filter((c) => c.type === options.type || c.configType === options.type);
29713
+ }
29714
+ if (configs.length === 0) {
29715
+ console.log(source_default.yellow("No discovered configs found matching your filters."));
29716
+ console.log(source_default.dim("Run `gal discover --scan` to trigger a discovery scan."));
29717
+ process.exit(0);
29718
+ }
29719
+ console.log(source_default.bold(`
29720
+ Discovered configs to approve (${orgName2}):
29721
+ `));
29722
+ for (const c of configs.slice(0, 20)) {
29723
+ const repoLabel = c.repo || c.repoName || "(unknown repo)";
29724
+ const typeLabel = c.type || c.configType || c.platform || "?";
29725
+ const nameLabel = c.name || c.path || "(unnamed)";
29726
+ console.log(` ${source_default.cyan(repoLabel)} / ${source_default.dim(typeLabel)} : ${nameLabel}`);
29727
+ }
29728
+ if (configs.length > 20) {
29729
+ console.log(source_default.dim(` ... and ${configs.length - 20} more`));
29730
+ }
29731
+ console.log();
29732
+ if (options.dryRun) {
29733
+ console.log(source_default.yellow(`Dry run: would approve ${configs.length} discovered configs.
29734
+ `));
29735
+ process.exit(0);
29736
+ }
29737
+ console.log(source_default.dim("To approve these configs, integrate them into your local .claude/ directory"));
29738
+ console.log(source_default.dim("and then run `gal approve` (without --from-discovered)."));
29739
+ console.log();
29740
+ console.log(source_default.dim("Direct auto-approval of discovered configs is coming in a future release."));
29741
+ process.exit(0);
29742
+ } catch (error2) {
29743
+ spinner2.stop();
29744
+ console.error(source_default.red(`Error: ${error2 instanceof Error ? error2.message : String(error2)}`));
29745
+ process.exit(1);
29746
+ }
29747
+ }
29365
29748
  const earlyConfig = ConfigManager.load();
29366
29749
  const isOrgMode = !!(options.org || options.clear || options.remove);
29367
29750
  const isLocalMode = options.local || !earlyConfig.authToken && !isOrgMode;
@@ -39179,6 +39562,54 @@ Discovery Results for "${org}"`));
39179
39562
  }
39180
39563
  }
39181
39564
  });
39565
+ command.command("show").description("Show content of a specific discovered config").argument("<repo>", 'Repository name (e.g. "my-org/my-repo")').argument("<path>", 'Config file path within the repository (e.g. "CLAUDE.md")').option("--org <orgName>", "Organization name (alternative to positional arg)").option("--format <format>", "Output format: text (default) or json", "text").action(async (repo, path8, options) => {
39566
+ const config = ConfigManager.load();
39567
+ const org = options.org || config.defaultOrg;
39568
+ if (!org) {
39569
+ console.error(source_default.red("No organization specified."));
39570
+ console.error(source_default.dim("Usage: gal discover show <repo> <path> --org <orgName>"));
39571
+ return;
39572
+ }
39573
+ const provider = new CoreServiceProvider({
39574
+ apiUrl: config.apiUrl || process.env.GAL_API_URL || "",
39575
+ authToken: config.authToken
39576
+ });
39577
+ const scanRepo = provider.getScanResultRepository();
39578
+ try {
39579
+ const cache = await scanRepo.getDiscoveredConfigsCache(org);
39580
+ const configs = cache?.configs || [];
39581
+ const match = configs.find((c) => {
39582
+ const repoMatch = c.repo === repo || c.repoName === repo || c.repo?.endsWith(`/${repo}`) || c.repoName?.endsWith(`/${repo}`);
39583
+ const pathMatch = c.path === path8 || c.filePath === path8 || c.path && c.path.endsWith(path8) || c.filePath && c.filePath.endsWith(path8);
39584
+ return repoMatch && pathMatch;
39585
+ });
39586
+ if (!match) {
39587
+ console.log(source_default.yellow(`Config not found: ${repo}/${path8}`));
39588
+ console.log(source_default.dim(`Run \`gal discover ${org}\` to see available configs.`));
39589
+ return;
39590
+ }
39591
+ if (options.format === "json") {
39592
+ console.log(JSON.stringify({ organization: org, repo, path: path8, config: match }, null, 2));
39593
+ } else {
39594
+ console.log(source_default.bold(`
39595
+ Config: ${repo}/${path8}
39596
+ `));
39597
+ if (match.platform) console.log(source_default.dim(`Platform: ${match.platform}`));
39598
+ if (match.type || match.configType) console.log(source_default.dim(`Type: ${match.type || match.configType}`));
39599
+ if (match.discoveredAt) console.log(source_default.dim(`Discovered: ${new Date(match.discoveredAt).toLocaleString()}`));
39600
+ console.log();
39601
+ const content = match.content || match.rawContent;
39602
+ if (content) {
39603
+ console.log(content);
39604
+ } else {
39605
+ console.log(source_default.dim("(No content available \u2014 run `gal discover --scan` to refresh.)"));
39606
+ }
39607
+ }
39608
+ } catch (error2) {
39609
+ const msg = error2 instanceof Error ? error2.message : String(error2);
39610
+ console.error(source_default.red(`Error: ${msg}`));
39611
+ }
39612
+ });
39182
39613
  return command;
39183
39614
  }
39184
39615
  var VALID_PLATFORMS, POLL_INTERVAL, SCAN_TIMEOUT;
@@ -44068,6 +44499,104 @@ function createPolicyCommand() {
44068
44499
  process.exit(1);
44069
44500
  }
44070
44501
  });
44502
+ command.command("show <id>").description("Show details of a specific policy (admin only)").option("--json", "Output as JSON").action(async (id, options) => {
44503
+ const config = ConfigManager.load();
44504
+ const authToken = config.authToken || config.apiKey;
44505
+ if (!authToken) {
44506
+ console.error(source_default.red("Not authenticated. Run: gal auth login"));
44507
+ process.exit(1);
44508
+ }
44509
+ const orgName = config.defaultOrg;
44510
+ if (!orgName) {
44511
+ console.error(source_default.red("No default organization set. Run: gal auth login"));
44512
+ process.exit(1);
44513
+ }
44514
+ const apiUrl = config.apiUrl || defaultApiUrl11;
44515
+ const spinner = options.json ? null : ora(`Fetching policy "${id}"...`).start();
44516
+ try {
44517
+ const res = await fetchWithAuth2(
44518
+ `${apiUrl}/organizations/${encodeURIComponent(orgName)}/agent-security-policies/${encodeURIComponent(id)}`,
44519
+ authToken
44520
+ );
44521
+ if (!res.ok) {
44522
+ const body = await res.json().catch(() => ({}));
44523
+ throw new Error(body.error || `HTTP ${res.status}`);
44524
+ }
44525
+ const data = await res.json();
44526
+ spinner?.stop();
44527
+ if (options.json) {
44528
+ console.log(JSON.stringify(data, null, 2));
44529
+ } else {
44530
+ const p = data.policy || data;
44531
+ console.log();
44532
+ console.log(` ${source_default.bold(p.name)} ${source_default.dim(`(${p.type || "policy"})`)}`);
44533
+ console.log(` ID: ${source_default.bold(p.id)}`);
44534
+ console.log(` Description: ${p.description || ""}`);
44535
+ console.log(` Enabled: ${p.enabled ? source_default.green("yes") : source_default.yellow("no")}`);
44536
+ console.log(` Version: ${p.version || "N/A"}`);
44537
+ console.log(` Created by: ${p.createdBy || "unknown"}`);
44538
+ if (p.allowedTools?.length > 0) {
44539
+ console.log(` Allowed tools: ${p.allowedTools.join(", ")}`);
44540
+ }
44541
+ if (p.blockedTools?.length > 0) {
44542
+ console.log(` Blocked tools: ${p.blockedTools.join(", ")}`);
44543
+ }
44544
+ console.log();
44545
+ }
44546
+ } catch (error2) {
44547
+ spinner?.stop();
44548
+ const msg = error2 instanceof Error ? error2.message : String(error2);
44549
+ if (options.json) {
44550
+ console.log(JSON.stringify({ error: msg }));
44551
+ } else {
44552
+ console.error(source_default.red("Error fetching policy:"), msg);
44553
+ }
44554
+ process.exit(1);
44555
+ }
44556
+ });
44557
+ command.command("activate <id>").description("Activate (enable) an agent security policy (admin only)").option("--json", "Output as JSON").action(async (id, options) => {
44558
+ const config = ConfigManager.load();
44559
+ const authToken = config.authToken || config.apiKey;
44560
+ if (!authToken) {
44561
+ console.error(source_default.red("Not authenticated. Run: gal auth login"));
44562
+ process.exit(1);
44563
+ }
44564
+ const orgName = config.defaultOrg;
44565
+ if (!orgName) {
44566
+ console.error(source_default.red("No default organization set. Run: gal auth login"));
44567
+ process.exit(1);
44568
+ }
44569
+ const apiUrl = config.apiUrl || defaultApiUrl11;
44570
+ const spinner = options.json ? null : ora(`Activating policy "${id}"...`).start();
44571
+ try {
44572
+ const res = await fetchWithAuth2(
44573
+ `${apiUrl}/organizations/${encodeURIComponent(orgName)}/agent-security-policies/${encodeURIComponent(id)}`,
44574
+ authToken,
44575
+ "PUT",
44576
+ { enabled: true }
44577
+ );
44578
+ if (!res.ok) {
44579
+ const data2 = await res.json().catch(() => ({}));
44580
+ throw new Error(data2.error || `HTTP ${res.status}`);
44581
+ }
44582
+ const data = await res.json();
44583
+ spinner?.succeed(`Policy "${id}" activated`);
44584
+ if (options.json) {
44585
+ console.log(JSON.stringify(data, null, 2));
44586
+ } else {
44587
+ console.log(` Policy ${source_default.bold(id)} is now ${source_default.green("enabled")}.`);
44588
+ }
44589
+ } catch (error2) {
44590
+ spinner?.stop();
44591
+ const msg = error2 instanceof Error ? error2.message : String(error2);
44592
+ if (options.json) {
44593
+ console.log(JSON.stringify({ error: msg }));
44594
+ } else {
44595
+ console.error(source_default.red("Error activating policy:"), msg);
44596
+ }
44597
+ process.exit(1);
44598
+ }
44599
+ });
44071
44600
  command.command("distribute <id>").description("Trigger distribution of an active policy").option("--json", "Output as JSON").action(async (id, options) => {
44072
44601
  const config = ConfigManager.load();
44073
44602
  const authToken = config.authToken || config.apiKey;
@@ -52743,14 +53272,63 @@ function printNoWorkspacesMessage() {
52743
53272
  console.log(" gal approve # Approve a local config");
52744
53273
  console.log(" gal sync # Sync approved config to all agents");
52745
53274
  }
53275
+ function switchWorkspaceAction(name, options) {
53276
+ const trimmedName = name.trim();
53277
+ const config = ConfigManager.load();
53278
+ const memberships = config.orgMemberships ?? [];
53279
+ if (!trimmedName) {
53280
+ if (options.json) {
53281
+ console.log(JSON.stringify({ error: "Workspace name cannot be empty." }));
53282
+ } else {
53283
+ console.error(source_default.red("Workspace name cannot be empty."));
53284
+ }
53285
+ process.exit(1);
53286
+ }
53287
+ if (memberships.length > 0 && !memberships.includes(trimmedName)) {
53288
+ if (options.json) {
53289
+ console.log(JSON.stringify({
53290
+ error: `Workspace "${trimmedName}" not found.`,
53291
+ available: memberships
53292
+ }));
53293
+ } else {
53294
+ console.error(
53295
+ source_default.red(
53296
+ `Workspace "${trimmedName}" not found. Run \`gal workspace list\` to see available workspaces.`
53297
+ )
53298
+ );
53299
+ }
53300
+ process.exit(1);
53301
+ }
53302
+ ConfigManager.save({
53303
+ ...config,
53304
+ defaultOrg: trimmedName
53305
+ });
53306
+ if (options.json) {
53307
+ console.log(JSON.stringify({ active: trimmedName }));
53308
+ } else {
53309
+ console.log(source_default.green(`\u2713 Switched to ${trimmedName}`));
53310
+ console.log(" Run `gal sync --pull` to sync the approved config for this workspace.");
53311
+ }
53312
+ }
52746
53313
  function createWorkspaceCommand() {
52747
53314
  const command = new Command("workspace");
52748
53315
  command.description("Manage workspaces (GitHub organizations and personal accounts)");
52749
- command.command("list").description("List all connected workspaces").action(() => {
53316
+ command.command("list").description("List all connected workspaces").option("--json", "Output as JSON").action((options) => {
52750
53317
  const config = ConfigManager.load();
52751
53318
  const memberships = config.orgMemberships ?? [];
52752
53319
  if (memberships.length === 0) {
52753
- printNoWorkspacesMessage();
53320
+ if (options.json) {
53321
+ console.log(JSON.stringify({ workspaces: [], active: null }));
53322
+ } else {
53323
+ printNoWorkspacesMessage();
53324
+ }
53325
+ return;
53326
+ }
53327
+ if (options.json) {
53328
+ console.log(JSON.stringify({
53329
+ workspaces: memberships,
53330
+ active: config.defaultOrg ?? null
53331
+ }));
52754
53332
  return;
52755
53333
  }
52756
53334
  for (const workspace of memberships) {
@@ -52760,36 +53338,100 @@ function createWorkspaceCommand() {
52760
53338
  console.log(`${prefix} ${workspace}${suffix}`);
52761
53339
  }
52762
53340
  });
52763
- command.command("current").description("Show the currently active workspace").action(() => {
53341
+ command.command("current").description("Show the currently active workspace").option("--json", "Output as JSON").action((options) => {
52764
53342
  const config = ConfigManager.load();
52765
53343
  if (!config.defaultOrg) {
52766
- console.log("No active workspace set. Run `gal workspace switch <name>` to set one.");
53344
+ if (options.json) {
53345
+ console.log(JSON.stringify({ active: null }));
53346
+ } else {
53347
+ console.log("No active workspace set. Run `gal workspace use <name>` to set one.");
53348
+ }
52767
53349
  return;
52768
53350
  }
52769
- console.log(`Active workspace: ${config.defaultOrg}`);
53351
+ if (options.json) {
53352
+ console.log(JSON.stringify({ active: config.defaultOrg }));
53353
+ } else {
53354
+ console.log(`Active workspace: ${config.defaultOrg}`);
53355
+ }
52770
53356
  });
52771
- command.command("switch").description("Switch the active workspace").argument("<name>", "Workspace name to activate").action((name) => {
52772
- const trimmedName = name.trim();
53357
+ command.command("use").description("Switch the active workspace").argument("<name>", "Workspace name to activate").option("--json", "Output as JSON").action((name, options) => {
53358
+ switchWorkspaceAction(name, options);
53359
+ });
53360
+ command.command("switch").description("Switch the active workspace (alias: use)").argument("<name>", "Workspace name to activate").option("--json", "Output as JSON").action((name, options) => {
53361
+ switchWorkspaceAction(name, options);
53362
+ });
53363
+ command.command("add").description("Add an organization to the workspace list").argument("<org>", "Organization name to add").option("--json", "Output as JSON").action((org, options) => {
53364
+ const trimmedOrg = org.trim();
53365
+ if (!trimmedOrg) {
53366
+ if (options.json) {
53367
+ console.log(JSON.stringify({ error: "Organization name cannot be empty." }));
53368
+ } else {
53369
+ console.error(source_default.red("Organization name cannot be empty."));
53370
+ }
53371
+ process.exit(1);
53372
+ }
52773
53373
  const config = ConfigManager.load();
52774
53374
  const memberships = config.orgMemberships ?? [];
52775
- if (!trimmedName) {
52776
- console.error(source_default.red("Workspace name cannot be empty."));
53375
+ if (memberships.includes(trimmedOrg)) {
53376
+ if (options.json) {
53377
+ console.log(JSON.stringify({ workspace: trimmedOrg, added: false, message: "Already in workspace list." }));
53378
+ } else {
53379
+ console.log(source_default.yellow(`"${trimmedOrg}" is already in your workspace list.`));
53380
+ }
53381
+ return;
53382
+ }
53383
+ ConfigManager.save({
53384
+ ...config,
53385
+ orgMemberships: [...memberships, trimmedOrg]
53386
+ });
53387
+ if (options.json) {
53388
+ console.log(JSON.stringify({ workspace: trimmedOrg, added: true }));
53389
+ } else {
53390
+ console.log(source_default.green(`\u2713 Added "${trimmedOrg}" to workspaces.`));
53391
+ console.log(source_default.dim(` Run \`gal workspace use ${trimmedOrg}\` to make it active.`));
53392
+ }
53393
+ });
53394
+ command.command("remove").description("Remove an organization from the workspace list").argument("<org>", "Organization name to remove").option("--json", "Output as JSON").action((org, options) => {
53395
+ const trimmedOrg = org.trim();
53396
+ if (!trimmedOrg) {
53397
+ if (options.json) {
53398
+ console.log(JSON.stringify({ error: "Organization name cannot be empty." }));
53399
+ } else {
53400
+ console.error(source_default.red("Organization name cannot be empty."));
53401
+ }
52777
53402
  process.exit(1);
52778
53403
  }
52779
- if (memberships.length > 0 && !memberships.includes(trimmedName)) {
52780
- console.error(
52781
- source_default.red(
52782
- `Workspace "${trimmedName}" not found. Run \`gal workspace list\` to see available workspaces.`
52783
- )
52784
- );
53404
+ const config = ConfigManager.load();
53405
+ const memberships = config.orgMemberships ?? [];
53406
+ if (!memberships.includes(trimmedOrg)) {
53407
+ if (options.json) {
53408
+ console.log(JSON.stringify({ error: `Workspace "${trimmedOrg}" not found.`, available: memberships }));
53409
+ } else {
53410
+ console.error(source_default.red(`Workspace "${trimmedOrg}" not found.`));
53411
+ }
52785
53412
  process.exit(1);
52786
53413
  }
52787
- ConfigManager.save({
53414
+ const updated = memberships.filter((m) => m !== trimmedOrg);
53415
+ const newConfig = {
52788
53416
  ...config,
52789
- defaultOrg: trimmedName
52790
- });
52791
- console.log(source_default.green(`\u2713 Switched to ${trimmedName}`));
52792
- console.log(" Run `gal sync --pull` to sync the approved config for this workspace.");
53417
+ orgMemberships: updated
53418
+ };
53419
+ if (config.defaultOrg === trimmedOrg) {
53420
+ delete newConfig.defaultOrg;
53421
+ }
53422
+ ConfigManager.save(newConfig);
53423
+ if (options.json) {
53424
+ console.log(JSON.stringify({
53425
+ workspace: trimmedOrg,
53426
+ removed: true,
53427
+ active: newConfig.defaultOrg ?? null
53428
+ }));
53429
+ } else {
53430
+ console.log(source_default.green(`\u2713 Removed "${trimmedOrg}" from workspaces.`));
53431
+ if (config.defaultOrg === trimmedOrg) {
53432
+ console.log(source_default.yellow(" Active workspace cleared. Run `gal workspace use <name>` to set a new one."));
53433
+ }
53434
+ }
52793
53435
  });
52794
53436
  return command;
52795
53437
  }
@@ -52809,6 +53451,7 @@ var init_command_registry = __esm({
52809
53451
  "use strict";
52810
53452
  init_admin();
52811
53453
  init_agent();
53454
+ init_approved_config();
52812
53455
  init_approve();
52813
53456
  init_audit();
52814
53457
  init_auth2();
@@ -52856,6 +53499,7 @@ var init_command_registry = __esm({
52856
53499
  COMMAND_FACTORIES = [
52857
53500
  { name: "admin", create: createAdminCommand },
52858
53501
  { name: "agent", create: createAgentCommand },
53502
+ { name: "approved-config", create: createApprovedConfigCommand },
52859
53503
  { name: "approve", create: createApproveCommand },
52860
53504
  { name: "audit", create: createAuditCommand },
52861
53505
  { name: "auth", create: createAuthCommand },
@@ -53377,7 +54021,7 @@ var init_index = __esm({
53377
54021
  });
53378
54022
 
53379
54023
  // src/bootstrap.ts
53380
- var cliVersion10 = true ? "0.0.263" : "0.0.0-dev";
54024
+ var cliVersion10 = true ? "0.0.266" : "0.0.0-dev";
53381
54025
  var args = process.argv.slice(2);
53382
54026
  var requestedGlobalHelp = args.length === 1 && (args[0] === "--help" || args[0] === "-h");
53383
54027
  var requestedVersion = args.length === 1 && (args[0] === "--version" || args[0] === "-V");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scheduler-systems/gal-run",
3
- "version": "0.0.263",
3
+ "version": "0.0.266",
4
4
  "description": "GAL CLI - Command-line tool for managing AI agent configurations across your organization",
5
5
  "license": "Elastic-2.0",
6
6
  "private": false,