@syntropic137/cli 0.20.1 → 0.21.0

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/syn.js +85 -65
  2. package/package.json +1 -1
package/dist/syn.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/config.ts
4
4
  var CLI_NAME = "syn";
5
5
  var CLI_DESCRIPTION = "Syntropic137 - Event-sourced workflow engine for AI agents";
6
- var CLI_VERSION = true ? "0.20.1" : "0.0.0-dev";
6
+ var CLI_VERSION = true ? "0.21.0" : "0.0.0-dev";
7
7
  var DEFAULT_TIMEOUT_MS = 3e4;
8
8
  var SSE_CONNECT_TIMEOUT_MS = 5e3;
9
9
  function getApiUrl() {
@@ -79,40 +79,40 @@ var SynClient = class {
79
79
  this.timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
80
80
  this.authHeaders = getAuthHeaders();
81
81
  }
82
- async get(path8, params) {
83
- const url = this.buildUrl(path8, params);
82
+ async get(path9, params) {
83
+ const url = this.buildUrl(path9, params);
84
84
  return this.request(url, { method: "GET" });
85
85
  }
86
- async post(path8, body, params) {
87
- const url = this.buildUrl(path8, params);
86
+ async post(path9, body, params) {
87
+ const url = this.buildUrl(path9, params);
88
88
  return this.request(url, {
89
89
  method: "POST",
90
90
  headers: { "Content-Type": "application/json" },
91
91
  ...body !== void 0 ? { body: JSON.stringify(body) } : {}
92
92
  });
93
93
  }
94
- async put(path8, body) {
95
- const url = this.buildUrl(path8);
94
+ async put(path9, body) {
95
+ const url = this.buildUrl(path9);
96
96
  return this.request(url, {
97
97
  method: "PUT",
98
98
  headers: { "Content-Type": "application/json" },
99
99
  ...body !== void 0 ? { body: JSON.stringify(body) } : {}
100
100
  });
101
101
  }
102
- async patch(path8, body) {
103
- const url = this.buildUrl(path8);
102
+ async patch(path9, body) {
103
+ const url = this.buildUrl(path9);
104
104
  return this.request(url, {
105
105
  method: "PATCH",
106
106
  headers: { "Content-Type": "application/json" },
107
107
  ...body !== void 0 ? { body: JSON.stringify(body) } : {}
108
108
  });
109
109
  }
110
- async delete(path8) {
111
- const url = this.buildUrl(path8);
110
+ async delete(path9) {
111
+ const url = this.buildUrl(path9);
112
112
  return this.request(url, { method: "DELETE" });
113
113
  }
114
- async stream(path8) {
115
- const url = this.buildUrl(path8);
114
+ async stream(path9) {
115
+ const url = this.buildUrl(path9);
116
116
  const controller = new AbortController();
117
117
  const timer = setTimeout(() => controller.abort(), SSE_CONNECT_TIMEOUT_MS);
118
118
  const response = await fetch(url.toString(), {
@@ -126,10 +126,10 @@ var SynClient = class {
126
126
  }
127
127
  return response.body;
128
128
  }
129
- buildUrl(path8, params) {
129
+ buildUrl(path9, params) {
130
130
  const base = new URL(this.baseUrl);
131
131
  const basePath = base.pathname.replace(/\/+$/, "");
132
- const reqPath = path8.startsWith("/") ? path8 : `/${path8}`;
132
+ const reqPath = path9.startsWith("/") ? path9 : `/${path9}`;
133
133
  const prefix = basePath.endsWith(API_PREFIX) ? "" : API_PREFIX;
134
134
  const url = new URL(`${basePath}${prefix}${reqPath}`, base);
135
135
  if (params) {
@@ -181,66 +181,66 @@ async function safeRequest(fn) {
181
181
  handleConnectError();
182
182
  }
183
183
  }
184
- async function apiGet(path8, options) {
184
+ async function apiGet(path9, options) {
185
185
  const client = new SynClient();
186
186
  const { status, data } = await safeRequest(
187
- () => client.get(path8, options?.params)
187
+ () => client.get(path9, options?.params)
188
188
  );
189
189
  checkResponse(status, data, options?.expected ?? [200]);
190
190
  return data;
191
191
  }
192
- async function apiGetList(path8, options) {
192
+ async function apiGetList(path9, options) {
193
193
  const client = new SynClient();
194
194
  const { status, data } = await safeRequest(
195
- () => client.get(path8, options?.params)
195
+ () => client.get(path9, options?.params)
196
196
  );
197
197
  checkResponse(status, data, options?.expected ?? [200]);
198
198
  return data;
199
199
  }
200
- async function apiGetPaginated(path8, key, options) {
200
+ async function apiGetPaginated(path9, key, options) {
201
201
  const client = new SynClient();
202
202
  const { status, data } = await safeRequest(
203
- () => client.get(path8, options?.params)
203
+ () => client.get(path9, options?.params)
204
204
  );
205
205
  checkResponse(status, data, options?.expected ?? [200]);
206
206
  if (typeof data !== "object" || data === null) {
207
- throw new CLIError(`Unexpected API response for "${path8}": expected an object containing "${key}".`);
207
+ throw new CLIError(`Unexpected API response for "${path9}": expected an object containing "${key}".`);
208
208
  }
209
209
  const items = data[key];
210
210
  if (!Array.isArray(items)) {
211
- throw new CLIError(`Unexpected API response for "${path8}": expected "${key}" to be an array.`);
211
+ throw new CLIError(`Unexpected API response for "${path9}": expected "${key}" to be an array.`);
212
212
  }
213
213
  return items;
214
214
  }
215
- async function apiPost(path8, options) {
215
+ async function apiPost(path9, options) {
216
216
  const client = new SynClient(
217
217
  options?.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : void 0
218
218
  );
219
219
  const { status, data } = await safeRequest(
220
- () => client.post(path8, options?.body, options?.params)
220
+ () => client.post(path9, options?.body, options?.params)
221
221
  );
222
- checkResponse(status, data, options?.expected ?? [200]);
222
+ checkResponse(status, data, options?.expected ?? [200, 201]);
223
223
  return data;
224
224
  }
225
- async function apiPut(path8, options) {
225
+ async function apiPut(path9, options) {
226
226
  const client = new SynClient();
227
227
  const { status, data } = await safeRequest(
228
- () => client.put(path8, options?.body)
228
+ () => client.put(path9, options?.body)
229
229
  );
230
230
  checkResponse(status, data, options?.expected ?? [200]);
231
231
  return data;
232
232
  }
233
- async function apiPatch(path8, options) {
233
+ async function apiPatch(path9, options) {
234
234
  const client = new SynClient();
235
235
  const { status, data } = await safeRequest(
236
- () => client.patch(path8, options?.body)
236
+ () => client.patch(path9, options?.body)
237
237
  );
238
238
  checkResponse(status, data, options?.expected ?? [200]);
239
239
  return data;
240
240
  }
241
- async function apiDelete(path8, options) {
241
+ async function apiDelete(path9, options) {
242
242
  const client = new SynClient();
243
- const { status, data } = await safeRequest(() => client.delete(path8));
243
+ const { status, data } = await safeRequest(() => client.delete(path9));
244
244
  checkResponse(status, data, options?.expected ?? [200]);
245
245
  return data;
246
246
  }
@@ -1103,6 +1103,7 @@ syn workflow run ${wfName}-v1 --task "Your task here"
1103
1103
  }
1104
1104
 
1105
1105
  // src/commands/workflow/crud.ts
1106
+ import path4 from "path";
1106
1107
  var createCommand = {
1107
1108
  name: "create",
1108
1109
  description: "Create a new workflow",
@@ -1219,8 +1220,9 @@ var validateCommand = {
1219
1220
  validatePackageDir(file);
1220
1221
  return;
1221
1222
  }
1223
+ const content = fs8.readFileSync(file, "utf-8");
1222
1224
  const data = await apiPost("/workflows/validate", {
1223
- body: { file }
1225
+ body: { content, filename: path4.basename(file) }
1224
1226
  });
1225
1227
  if (data["valid"]) {
1226
1228
  printSuccess("Valid workflow definition\n");
@@ -1492,11 +1494,11 @@ var statusCommand = {
1492
1494
 
1493
1495
  // src/commands/workflow/install.ts
1494
1496
  import fs5 from "fs";
1495
- import path5 from "path";
1497
+ import path6 from "path";
1496
1498
 
1497
1499
  // src/marketplace/client.ts
1498
1500
  import fs4 from "fs";
1499
- import path4 from "path";
1501
+ import path5 from "path";
1500
1502
 
1501
1503
  // src/marketplace/models.ts
1502
1504
  import { z as z2 } from "zod";
@@ -1553,7 +1555,7 @@ function saveRegistries(config) {
1553
1555
  }
1554
1556
  function loadCachedIndex(registryName) {
1555
1557
  validateRegistryName(registryName);
1556
- const cachePath = path4.join(CACHE_DIR, `${registryName}.json`);
1558
+ const cachePath = path5.join(CACHE_DIR, `${registryName}.json`);
1557
1559
  if (!fs4.existsSync(cachePath)) return null;
1558
1560
  try {
1559
1561
  const content = fs4.readFileSync(cachePath, "utf-8");
@@ -1565,7 +1567,7 @@ function loadCachedIndex(registryName) {
1565
1567
  function saveCachedIndex(registryName, cached) {
1566
1568
  validateRegistryName(registryName);
1567
1569
  fs4.mkdirSync(CACHE_DIR, { recursive: true });
1568
- const cachePath = path4.join(CACHE_DIR, `${registryName}.json`);
1570
+ const cachePath = path5.join(CACHE_DIR, `${registryName}.json`);
1569
1571
  writeJsonFile(cachePath, cached);
1570
1572
  }
1571
1573
  function isCacheStale(cached) {
@@ -1582,7 +1584,7 @@ async function fetchMarketplaceJson(repo, ref = "main") {
1582
1584
  const tmpdir = makeTempDir("syn-mkt-");
1583
1585
  try {
1584
1586
  await gitClone(url, ref, tmpdir);
1585
- const marketplacePath = path4.join(tmpdir, "marketplace.json");
1587
+ const marketplacePath = path5.join(tmpdir, "marketplace.json");
1586
1588
  if (!fs4.existsSync(marketplacePath)) {
1587
1589
  throw new Error(`No marketplace.json found in ${repo}`);
1588
1590
  }
@@ -1694,7 +1696,7 @@ async function tryMarketplaceResolution(source, ref) {
1694
1696
  removeTempDir(tmpdir);
1695
1697
  throw err;
1696
1698
  }
1697
- const subdir = path5.resolve(tmpdir, plugin.source.replace(/^\.\//, ""));
1699
+ const subdir = path6.resolve(tmpdir, plugin.source.replace(/^\.\//, ""));
1698
1700
  if (!subdir.startsWith(tmpdir)) {
1699
1701
  removeTempDir(tmpdir);
1700
1702
  throw new Error(`Plugin source path escapes repository: ${plugin.source}`);
@@ -1710,7 +1712,7 @@ async function resolveSource(source, ref) {
1710
1712
  const { tmpdir, manifest: manifest2, workflows: workflows2 } = await resolveFromGit(resolved, ref);
1711
1713
  return { packagePath: tmpdir, manifest: manifest2, workflows: workflows2, tmpdir };
1712
1714
  }
1713
- const packagePath = path5.resolve(resolved);
1715
+ const packagePath = path6.resolve(resolved);
1714
1716
  const { manifest, workflows } = resolvePackage(packagePath);
1715
1717
  return { packagePath, manifest, workflows, tmpdir: null };
1716
1718
  }
@@ -1788,7 +1790,7 @@ var installCommand = {
1788
1790
  throw new CLIError("No workflows", 1);
1789
1791
  }
1790
1792
  const fmt = detectFormat(packagePath);
1791
- const pkgName = manifest?.name ?? path5.basename(packagePath);
1793
+ const pkgName = manifest?.name ?? path6.basename(packagePath);
1792
1794
  const pkgVersion = manifest?.version ?? "0.0.0";
1793
1795
  printPackagePreview(pkgName, pkgVersion, source, fmt, workflows);
1794
1796
  if (dryRun) {
@@ -1881,11 +1883,11 @@ var initCommand = {
1881
1883
  },
1882
1884
  handler: async (parsed) => {
1883
1885
  const directory = parsed.positionals[0] ?? ".";
1884
- const resolvedDir = path5.resolve(directory);
1886
+ const resolvedDir = path6.resolve(directory);
1885
1887
  const workflowType = parsed.values["type"] ?? "research";
1886
1888
  const numPhases = parseInt(parsed.values["phases"] ?? "3", 10);
1887
1889
  const multi = parsed.values["multi"] === true;
1888
- const wfName = parsed.values["name"] ?? path5.basename(resolvedDir).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
1890
+ const wfName = parsed.values["name"] ?? path6.basename(resolvedDir).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
1889
1891
  if (fs5.existsSync(resolvedDir)) {
1890
1892
  const entries = fs5.readdirSync(resolvedDir);
1891
1893
  if (entries.length > 0) {
@@ -1909,7 +1911,7 @@ var initCommand = {
1909
1911
 
1910
1912
  // src/commands/workflow/export.ts
1911
1913
  import fs6 from "fs";
1912
- import path6 from "path";
1914
+ import path7 from "path";
1913
1915
  var exportCommand = {
1914
1916
  name: "export",
1915
1917
  description: "Export a workflow as a distributable package or Claude Code plugin",
@@ -1939,7 +1941,7 @@ var exportCommand = {
1939
1941
  printError("Export returned no files");
1940
1942
  throw new CLIError("Export empty", 1);
1941
1943
  }
1942
- const outDir = path6.resolve(outputDir);
1944
+ const outDir = path7.resolve(outputDir);
1943
1945
  if (fs6.existsSync(outDir)) {
1944
1946
  const entries = fs6.readdirSync(outDir);
1945
1947
  if (entries.length > 0) {
@@ -1952,12 +1954,12 @@ var exportCommand = {
1952
1954
  printError(`Unsafe file path in export manifest: ${relPath}`);
1953
1955
  throw new CLIError("Path traversal", 1);
1954
1956
  }
1955
- const filePath = path6.resolve(outDir, relPath);
1957
+ const filePath = path7.resolve(outDir, relPath);
1956
1958
  if (!filePath.startsWith(outDir)) {
1957
1959
  printError(`Path escapes output directory: ${relPath}`);
1958
1960
  throw new CLIError("Path traversal", 1);
1959
1961
  }
1960
- fs6.mkdirSync(path6.dirname(filePath), { recursive: true });
1962
+ fs6.mkdirSync(path7.dirname(filePath), { recursive: true });
1961
1963
  fs6.writeFileSync(filePath, content, "utf-8");
1962
1964
  }
1963
1965
  const workflowName = data.workflow_name ?? workflowId;
@@ -1968,7 +1970,7 @@ var exportCommand = {
1968
1970
  print(` Output: ${outDir}`);
1969
1971
  print(` Files: ${Object.keys(files).length}`);
1970
1972
  print("");
1971
- print(style(`${path6.basename(outDir)}/`, CYAN));
1973
+ print(style(`${path7.basename(outDir)}/`, CYAN));
1972
1974
  const sortedPaths = Object.keys(files).sort();
1973
1975
  for (const relPath of sortedPaths) {
1974
1976
  const parts = relPath.split("/");
@@ -2242,7 +2244,7 @@ workflowGroup.command(createCommand).command(listCommand).command(showCommand).c
2242
2244
 
2243
2245
  // src/commands/marketplace/registry.ts
2244
2246
  import fs7 from "fs";
2245
- import path7 from "path";
2247
+ import path8 from "path";
2246
2248
  var addCommand = {
2247
2249
  name: "add",
2248
2250
  description: "Register a GitHub repo as a workflow marketplace",
@@ -2347,7 +2349,7 @@ var removeCommand = {
2347
2349
  saveRegistries({ version: config.version, registries: remaining });
2348
2350
  try {
2349
2351
  validateRegistryName(name);
2350
- const cachePath = path7.join(synPath("marketplace", "cache"), `${name}.json`);
2352
+ const cachePath = path8.join(synPath("marketplace", "cache"), `${name}.json`);
2351
2353
  if (fs7.existsSync(cachePath)) fs7.unlinkSync(cachePath);
2352
2354
  } catch {
2353
2355
  }
@@ -2491,7 +2493,7 @@ var createCommand2 = {
2491
2493
  description: "Create a new artifact",
2492
2494
  options: {
2493
2495
  workflow: { type: "string", short: "w", description: "Workflow ID" },
2494
- type: { type: "string", short: "t", description: "Artifact type (code, document, research_summary)" },
2496
+ type: { type: "string", short: "t", description: "Artifact type (code, markdown, text, json, yaml, research_summary, plan, other)" },
2495
2497
  title: { type: "string", description: "Artifact title" },
2496
2498
  content: { type: "string", short: "c", description: "Artifact content" },
2497
2499
  phase: { type: "string", short: "p", description: "Phase ID" }
@@ -2755,6 +2757,10 @@ var metadataCommand = {
2755
2757
  throw new CLIError("Missing argument", 1);
2756
2758
  }
2757
2759
  const m = await apiGet(`/conversations/${sessionId}/metadata`);
2760
+ if (!m || !m.session_id) {
2761
+ printError("Session not found or no metadata available.");
2762
+ throw new CLIError("Not found", 1);
2763
+ }
2758
2764
  print(`${style("Metadata:", BOLD)} ${m.session_id}`);
2759
2765
  if (m.model != null) print(` Model: ${m.model}`);
2760
2766
  if (m.event_count != null) print(` Events: ${m.event_count.toLocaleString()}`);
@@ -2940,10 +2946,17 @@ function reqSessionId(parsed) {
2940
2946
  var recentCommand = {
2941
2947
  name: "recent",
2942
2948
  description: "Show recent domain events across all sessions",
2943
- options: { limit: { type: "string", description: "Max events (max 200)", default: "50" } },
2949
+ options: {
2950
+ limit: { type: "string", description: "Max events (max 200)", default: "50" },
2951
+ type: { type: "string", short: "t", description: "Filter by event type" }
2952
+ },
2944
2953
  handler: async (parsed) => {
2945
2954
  const limit = parsed.values["limit"] ?? "50";
2946
- const data = await apiGet("/events/recent", { params: { limit } });
2955
+ const params = buildParams({
2956
+ limit,
2957
+ event_type: parsed.values["type"] ?? null
2958
+ });
2959
+ const data = await apiGet("/events/recent", { params });
2947
2960
  if (data.events.length === 0) {
2948
2961
  printDim("No recent events.");
2949
2962
  return;
@@ -3545,11 +3558,14 @@ var registerCommand = {
3545
3558
  printError("Missing --url");
3546
3559
  throw new CLIError("Missing option", 1);
3547
3560
  }
3548
- const body = { repo_url: url };
3549
- const system = parsed.values["system"];
3550
3561
  const org = parsed.values["org"];
3562
+ if (!org) {
3563
+ printError("Missing --org");
3564
+ throw new CLIError("Missing option", 1);
3565
+ }
3566
+ const body = { full_name: url, organization_id: org };
3567
+ const system = parsed.values["system"];
3551
3568
  if (system) body["system_id"] = system;
3552
- if (org) body["organization_id"] = org;
3553
3569
  const d = await apiPost("/repos", { body, expected: [200, 201] });
3554
3570
  printSuccess(`Repository registered: ${d["repo_id"] ?? ""}`);
3555
3571
  print(` Name: ${d["full_name"] ?? url}`);
@@ -4187,8 +4203,8 @@ function reqId4(parsed) {
4187
4203
  function parseConditions(condStrs) {
4188
4204
  return condStrs.map((c) => {
4189
4205
  const eqIdx = c.indexOf("=");
4190
- if (eqIdx < 1) throw new CLIError(`Invalid condition format: ${c} (expected key=value)`, 1);
4191
- return { key: c.slice(0, eqIdx), value: c.slice(eqIdx + 1) };
4206
+ if (eqIdx < 1) throw new CLIError(`Invalid condition format: ${c} (expected field=value)`, 1);
4207
+ return { field: c.slice(0, eqIdx), operator: "eq", value: c.slice(eqIdx + 1) };
4192
4208
  });
4193
4209
  }
4194
4210
  var registerCommand2 = {
@@ -4198,7 +4214,7 @@ var registerCommand2 = {
4198
4214
  repo: { type: "string", short: "r", description: "Repository ID" },
4199
4215
  workflow: { type: "string", short: "w", description: "Workflow ID to execute" },
4200
4216
  event: { type: "string", short: "e", description: "GitHub event type (e.g. check_run.completed)" },
4201
- condition: { type: "string", short: "c", multiple: true, description: "Condition as key=value (repeatable)" },
4217
+ condition: { type: "string", short: "c", multiple: true, description: "Condition as field=value (repeatable, uses 'eq' operator)" },
4202
4218
  "max-fires": { type: "string", description: "Maximum fires per period", default: "5" },
4203
4219
  cooldown: { type: "string", description: "Cooldown in seconds", default: "300" },
4204
4220
  budget: { type: "string", description: "Budget limit in USD" }
@@ -4239,9 +4255,10 @@ var registerCommand2 = {
4239
4255
  var enablePresetCommand = {
4240
4256
  name: "enable",
4241
4257
  description: "Enable a built-in trigger preset",
4242
- args: [{ name: "preset", description: "Preset name (self-healing, review-fix)", required: true }],
4258
+ args: [{ name: "preset", description: "Preset name (self-healing, review-fix, comment-command)", required: true }],
4243
4259
  options: {
4244
- repo: { type: "string", short: "r", description: "Repository ID" }
4260
+ repo: { type: "string", short: "r", description: "Repository ID" },
4261
+ workflow: { type: "string", short: "w", description: "Workflow ID to dispatch (default: preset default)" }
4245
4262
  },
4246
4263
  handler: async (parsed) => {
4247
4264
  const preset = parsed.positionals[0];
@@ -4254,7 +4271,10 @@ var enablePresetCommand = {
4254
4271
  printError("Missing --repo");
4255
4272
  throw new CLIError("Missing option", 1);
4256
4273
  }
4257
- const d = await apiPost(`/triggers/presets/${encodeURIComponent(preset)}`, { body: { repository: repo }, expected: [200, 201] });
4274
+ const workflow = parsed.values["workflow"];
4275
+ const body = { repository: repo };
4276
+ if (workflow) body["workflow_id"] = workflow;
4277
+ const d = await apiPost(`/triggers/presets/${encodeURIComponent(preset)}`, { body, expected: [200, 201] });
4258
4278
  printSuccess(`Preset "${preset}" enabled: ${d["trigger_id"] ?? ""}`);
4259
4279
  }
4260
4280
  };
@@ -4315,7 +4335,7 @@ var showCommand11 = {
4315
4335
  if (conditions.length > 0) {
4316
4336
  print(style(" Conditions:", BOLD));
4317
4337
  for (const c of conditions) {
4318
- print(` ${c["key"] ?? ""} = ${c["value"] ?? ""}`);
4338
+ print(` ${c["field"] ?? ""} = ${c["value"] ?? ""}`);
4319
4339
  }
4320
4340
  }
4321
4341
  }
@@ -4437,9 +4457,9 @@ function parseSseLine(line) {
4437
4457
  return null;
4438
4458
  }
4439
4459
  }
4440
- async function* streamSSE(path8) {
4460
+ async function* streamSSE(path9) {
4441
4461
  const client = new SynClient();
4442
- const body = await client.stream(path8);
4462
+ const body = await client.stream(path9);
4443
4463
  const reader = body.pipeThrough(new TextDecoderStream()).getReader();
4444
4464
  let buffer = "";
4445
4465
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syntropic137/cli",
3
- "version": "0.20.1",
3
+ "version": "0.21.0",
4
4
  "description": "Syntropic137 CLI - Event-sourced workflow engine for AI agents",
5
5
  "type": "module",
6
6
  "bin": {