@zivis/cli 0.1.0-alpha.5 → 0.1.0-alpha.52

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 (197) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +31 -0
  3. package/dist/commands/app/index.d.ts +0 -8
  4. package/dist/commands/app/index.js +8 -18
  5. package/dist/commands/assurance/index.d.ts +35 -0
  6. package/dist/commands/assurance/index.js +154 -0
  7. package/dist/commands/auth/bind.d.ts +4 -0
  8. package/dist/commands/auth/bind.js +97 -0
  9. package/dist/commands/auth/index.js +21 -2
  10. package/dist/commands/auth/init.d.ts +2 -0
  11. package/dist/commands/auth/init.js +248 -96
  12. package/dist/commands/auth/login.d.ts +2 -0
  13. package/dist/commands/auth/login.js +136 -18
  14. package/dist/commands/auth/logout.js +19 -7
  15. package/dist/commands/auth/sessions.js +7 -4
  16. package/dist/commands/auth/status.js +33 -12
  17. package/dist/commands/auth/token.d.ts +4 -0
  18. package/dist/commands/auth/token.js +73 -0
  19. package/dist/commands/check/index.d.ts +2 -0
  20. package/dist/commands/check/index.js +463 -0
  21. package/dist/commands/credits/index.js +2 -3
  22. package/dist/commands/gate/index.d.ts +2 -0
  23. package/dist/commands/gate/index.js +171 -0
  24. package/dist/commands/inspect/index.d.ts +2 -0
  25. package/dist/commands/inspect/index.js +136 -0
  26. package/dist/commands/mcp/index.js +113 -14
  27. package/dist/commands/open/index.d.ts +0 -8
  28. package/dist/commands/open/index.js +0 -11
  29. package/dist/commands/run/index.d.ts +2 -0
  30. package/dist/commands/run/index.js +206 -0
  31. package/dist/commands/scan/index.js +3 -8
  32. package/dist/commands/share/index.d.ts +14 -0
  33. package/dist/commands/share/index.js +100 -0
  34. package/dist/commands/sync/index.d.ts +2 -0
  35. package/dist/commands/sync/index.js +193 -0
  36. package/dist/commands/target/index.js +0 -3
  37. package/dist/commands/test/index.d.ts +2 -0
  38. package/dist/commands/test/index.js +198 -0
  39. package/dist/commands/threatmodel/index.d.ts +2 -0
  40. package/dist/commands/threatmodel/index.js +167 -0
  41. package/dist/index.js +28 -3
  42. package/dist/internal/application-binding.d.ts +8 -0
  43. package/dist/internal/application-binding.js +167 -0
  44. package/dist/internal/binding-token-mint.d.ts +33 -0
  45. package/dist/internal/binding-token-mint.js +174 -0
  46. package/dist/internal/change-recommendation.d.ts +6 -0
  47. package/dist/internal/change-recommendation.js +113 -0
  48. package/dist/internal/cli-output.d.ts +16 -0
  49. package/dist/internal/cli-output.js +39 -0
  50. package/dist/internal/devx-run.d.ts +129 -0
  51. package/dist/internal/devx-run.js +39 -0
  52. package/dist/internal/extractor/adapters/go/index.d.ts +0 -4
  53. package/dist/internal/extractor/adapters/go/index.js +0 -6
  54. package/dist/internal/extractor/adapters/go/manifest.d.ts +0 -17
  55. package/dist/internal/extractor/adapters/go/manifest.js +19 -25
  56. package/dist/internal/extractor/adapters/go/parser.d.ts +0 -4
  57. package/dist/internal/extractor/adapters/go/parser.js +0 -5
  58. package/dist/internal/extractor/adapters/go/scanner.d.ts +0 -20
  59. package/dist/internal/extractor/adapters/go/scanner.js +0 -43
  60. package/dist/internal/extractor/adapters/python/index.d.ts +0 -3
  61. package/dist/internal/extractor/adapters/python/index.js +0 -4
  62. package/dist/internal/extractor/adapters/python/manifest.d.ts +0 -8
  63. package/dist/internal/extractor/adapters/python/manifest.js +21 -18
  64. package/dist/internal/extractor/adapters/python/parser.d.ts +0 -3
  65. package/dist/internal/extractor/adapters/python/parser.js +0 -4
  66. package/dist/internal/extractor/adapters/python/scanner.d.ts +0 -18
  67. package/dist/internal/extractor/adapters/python/scanner.js +1 -54
  68. package/dist/internal/extractor/adapters/typescript/index.d.ts +0 -7
  69. package/dist/internal/extractor/adapters/typescript/index.js +1 -21
  70. package/dist/internal/extractor/adapters/typescript/manifest.d.ts +0 -14
  71. package/dist/internal/extractor/adapters/typescript/manifest.js +26 -17
  72. package/dist/internal/extractor/adapters/typescript/parser.d.ts +0 -8
  73. package/dist/internal/extractor/adapters/typescript/parser.js +0 -9
  74. package/dist/internal/extractor/adapters/typescript/scanner.d.ts +0 -24
  75. package/dist/internal/extractor/adapters/typescript/scanner.js +0 -43
  76. package/dist/internal/extractor/graph-builder.d.ts +10 -0
  77. package/dist/internal/extractor/graph-builder.js +245 -0
  78. package/dist/internal/extractor/graph-types.d.ts +89 -0
  79. package/dist/internal/extractor/graph-types.js +1 -0
  80. package/dist/internal/extractor/index.d.ts +0 -14
  81. package/dist/internal/extractor/index.js +0 -13
  82. package/dist/internal/extractor/types.d.ts +0 -45
  83. package/dist/internal/extractor/types.js +0 -27
  84. package/dist/internal/extractor/walker.d.ts +0 -18
  85. package/dist/internal/extractor/walker.js +0 -18
  86. package/dist/internal/gate-evaluate.d.ts +50 -0
  87. package/dist/internal/gate-evaluate.js +111 -0
  88. package/dist/internal/gate-policy.d.ts +38 -0
  89. package/dist/internal/gate-policy.js +167 -0
  90. package/dist/internal/git-metadata.d.ts +8 -0
  91. package/dist/internal/git-metadata.js +38 -0
  92. package/dist/internal/git.d.ts +9 -0
  93. package/dist/internal/git.js +45 -0
  94. package/dist/internal/github-linkage-prompt.d.ts +7 -0
  95. package/dist/internal/github-linkage-prompt.js +95 -0
  96. package/dist/internal/ide-setup.d.ts +17 -7
  97. package/dist/internal/ide-setup.js +379 -46
  98. package/dist/internal/inventory-sync.d.ts +74 -0
  99. package/dist/internal/inventory-sync.js +189 -0
  100. package/dist/internal/org-picker.d.ts +11 -0
  101. package/dist/internal/org-picker.js +34 -0
  102. package/dist/internal/orgs.js +0 -1
  103. package/dist/internal/packs/cache.d.ts +8 -0
  104. package/dist/internal/packs/cache.js +60 -0
  105. package/dist/internal/packs/index.d.ts +10 -0
  106. package/dist/internal/packs/index.js +8 -0
  107. package/dist/internal/packs/integrity.d.ts +9 -0
  108. package/dist/internal/packs/integrity.js +24 -0
  109. package/dist/internal/packs/jcs.d.ts +2 -0
  110. package/dist/internal/packs/jcs.js +57 -0
  111. package/dist/internal/packs/local-paths.d.ts +4 -0
  112. package/dist/internal/packs/local-paths.js +26 -0
  113. package/dist/internal/packs/npm-registry-client.d.ts +14 -0
  114. package/dist/internal/packs/npm-registry-client.js +87 -0
  115. package/dist/internal/packs/registry-client.d.ts +9 -0
  116. package/dist/internal/packs/registry-client.js +25 -0
  117. package/dist/internal/packs/resolve.d.ts +8 -0
  118. package/dist/internal/packs/resolve.js +89 -0
  119. package/dist/internal/packs/semver.d.ts +9 -0
  120. package/dist/internal/packs/semver.js +57 -0
  121. package/dist/internal/packs/signing.d.ts +5 -0
  122. package/dist/internal/packs/signing.js +56 -0
  123. package/dist/internal/packs/types.d.ts +77 -0
  124. package/dist/internal/packs/types.js +20 -0
  125. package/dist/internal/project-binding.d.ts +1 -0
  126. package/dist/internal/project-binding.js +16 -11
  127. package/dist/internal/resolve-init-session.d.ts +8 -0
  128. package/dist/internal/resolve-init-session.js +36 -0
  129. package/dist/internal/run-workdir.d.ts +2 -0
  130. package/dist/internal/run-workdir.js +19 -0
  131. package/dist/internal/security-context.d.ts +63 -0
  132. package/dist/internal/security-context.js +50 -0
  133. package/dist/internal/sync-outbox.d.ts +40 -0
  134. package/dist/internal/sync-outbox.js +66 -0
  135. package/dist/internal/target-auth/api-config.d.ts +0 -17
  136. package/dist/internal/target-auth/api-config.js +0 -18
  137. package/dist/internal/target-auth/config.d.ts +0 -12
  138. package/dist/internal/target-auth/config.js +0 -13
  139. package/dist/internal/target-auth/index.d.ts +0 -18
  140. package/dist/internal/target-auth/index.js +0 -19
  141. package/dist/internal/target-auth/keychain.d.ts +0 -18
  142. package/dist/internal/target-auth/keychain.js +3 -23
  143. package/dist/internal/target-auth/types.d.ts +0 -26
  144. package/dist/internal/target-auth/types.js +0 -10
  145. package/dist/internal/target-auth/workos.d.ts +0 -15
  146. package/dist/internal/target-auth/workos.js +9 -18
  147. package/dist/internal/test-scope.d.ts +99 -0
  148. package/dist/internal/test-scope.js +101 -0
  149. package/dist/internal/threatmodel-run-state.d.ts +15 -0
  150. package/dist/internal/threatmodel-run-state.js +43 -0
  151. package/dist/internal/threatmodel-run.d.ts +85 -0
  152. package/dist/internal/threatmodel-run.js +159 -0
  153. package/dist/internal/trust-room-link.d.ts +10 -0
  154. package/dist/internal/trust-room-link.js +15 -0
  155. package/dist/internal/zivis-local-state.d.ts +1 -0
  156. package/dist/internal/zivis-local-state.js +22 -0
  157. package/package.json +21 -5
  158. package/dist/commands/app/index.js.map +0 -1
  159. package/dist/commands/auth/index.js.map +0 -1
  160. package/dist/commands/auth/init.js.map +0 -1
  161. package/dist/commands/auth/login.js.map +0 -1
  162. package/dist/commands/auth/logout.js.map +0 -1
  163. package/dist/commands/auth/sessions.js.map +0 -1
  164. package/dist/commands/auth/status.js.map +0 -1
  165. package/dist/commands/credits/index.js.map +0 -1
  166. package/dist/commands/mcp/index.js.map +0 -1
  167. package/dist/commands/open/index.js.map +0 -1
  168. package/dist/commands/scan/index.js.map +0 -1
  169. package/dist/commands/target/index.js.map +0 -1
  170. package/dist/commands/tm/index.d.ts +0 -2
  171. package/dist/commands/tm/index.js +0 -266
  172. package/dist/commands/tm/index.js.map +0 -1
  173. package/dist/index.js.map +0 -1
  174. package/dist/internal/extractor/adapters/go/index.js.map +0 -1
  175. package/dist/internal/extractor/adapters/go/manifest.js.map +0 -1
  176. package/dist/internal/extractor/adapters/go/parser.js.map +0 -1
  177. package/dist/internal/extractor/adapters/go/scanner.js.map +0 -1
  178. package/dist/internal/extractor/adapters/python/index.js.map +0 -1
  179. package/dist/internal/extractor/adapters/python/manifest.js.map +0 -1
  180. package/dist/internal/extractor/adapters/python/parser.js.map +0 -1
  181. package/dist/internal/extractor/adapters/python/scanner.js.map +0 -1
  182. package/dist/internal/extractor/adapters/typescript/index.js.map +0 -1
  183. package/dist/internal/extractor/adapters/typescript/manifest.js.map +0 -1
  184. package/dist/internal/extractor/adapters/typescript/parser.js.map +0 -1
  185. package/dist/internal/extractor/adapters/typescript/scanner.js.map +0 -1
  186. package/dist/internal/extractor/index.js.map +0 -1
  187. package/dist/internal/extractor/types.js.map +0 -1
  188. package/dist/internal/extractor/walker.js.map +0 -1
  189. package/dist/internal/ide-setup.js.map +0 -1
  190. package/dist/internal/orgs.js.map +0 -1
  191. package/dist/internal/project-binding.js.map +0 -1
  192. package/dist/internal/target-auth/api-config.js.map +0 -1
  193. package/dist/internal/target-auth/config.js.map +0 -1
  194. package/dist/internal/target-auth/index.js.map +0 -1
  195. package/dist/internal/target-auth/keychain.js.map +0 -1
  196. package/dist/internal/target-auth/types.js.map +0 -1
  197. package/dist/internal/target-auth/workos.js.map +0 -1
@@ -0,0 +1,206 @@
1
+ import * as fs from "node:fs/promises";
2
+ import { ApiClient } from "@zivis/mcp/api-client";
3
+ import { resolveConfigFromBinding } from "@zivis/mcp/project-binding";
4
+ import { DEVX_RUN_CONTRACT_VERSION, completeDevXRunRemote, cancelDevXRunRemote, reportDevXRunItems, isRetryableDevXRunError, } from "../../internal/devx-run.js";
5
+ import { writeDevXRunCompleteOutboxEntry } from "../../internal/sync-outbox.js";
6
+ import { jsonEnvelope, printJson, EXIT, errorMessage, failCommand } from "../../internal/cli-output.js";
7
+ import { trustRoomJsonFields, trustRoomHumanLines } from "../../internal/trust-room-link.js";
8
+ async function readInput(inputArg) {
9
+ const raw = inputArg === "-" ? await readStdin() : await fs.readFile(inputArg, "utf-8");
10
+ try {
11
+ return JSON.parse(raw);
12
+ }
13
+ catch (err) {
14
+ throw new Error(`--input is not valid JSON: ${errorMessage(err)}`);
15
+ }
16
+ }
17
+ async function readStdin() {
18
+ let data = "";
19
+ process.stdin.setEncoding("utf-8");
20
+ for await (const chunk of process.stdin) {
21
+ data += chunk;
22
+ }
23
+ return data;
24
+ }
25
+ export function registerRunCommands(program) {
26
+ const run = program.command("run").description("resolve a DevX run started by a run producer (e.g. `zivis test`)");
27
+ run
28
+ .command("complete <runId>")
29
+ .description("complete a pending run with its result envelope")
30
+ .requiredOption("--input <path>", "path to a JSON result file, or '-' for stdin")
31
+ .option("--json", "emit JSON only on stdout")
32
+ .action(async (runId, opts) => {
33
+ const json = !!opts.json;
34
+ let result;
35
+ try {
36
+ result = await readInput(opts.input);
37
+ }
38
+ catch (err) {
39
+ failCommand("run complete", err, json);
40
+ }
41
+ const config = resolveConfigFromBinding(process.cwd());
42
+ const client = new ApiClient(config);
43
+ try {
44
+ const response = await completeDevXRunRemote(client, {
45
+ runId,
46
+ contractVersion: DEVX_RUN_CONTRACT_VERSION,
47
+ result,
48
+ });
49
+ const reconciliation = response.reconciliation;
50
+ const output = jsonEnvelope("run complete", {
51
+ run_id: runId,
52
+ outcome: response.outcome,
53
+ status: response.run.status,
54
+ completed_at: response.run.completedAt,
55
+ ...(reconciliation
56
+ ? {
57
+ finding_ids: reconciliation.findings.map((f) => ({ id: f.id, action: f.action })),
58
+ retest_results: reconciliation.retests,
59
+ evidence_ids: reconciliation.evidence.map((e) => ({ id: e.id, action: e.action })),
60
+ coverage: reconciliation.coverage,
61
+ }
62
+ : {}),
63
+ ...trustRoomJsonFields(response),
64
+ });
65
+ if (json) {
66
+ printJson(output);
67
+ }
68
+ else {
69
+ console.log(`zivis run complete: ${response.outcome} (run ${runId} is now ${response.run.status})`);
70
+ if (reconciliation) {
71
+ const created = reconciliation.findings.filter((f) => f.action === "created").length;
72
+ const updated = reconciliation.findings.filter((f) => f.action === "updated").length;
73
+ const retested = reconciliation.retests.filter((r) => r.action === "disposition_updated").length;
74
+ if (created)
75
+ console.log(` + ${created} new finding${created === 1 ? "" : "s"}`);
76
+ if (updated)
77
+ console.log(` ~ ${updated} finding${updated === 1 ? "" : "s"} updated`);
78
+ if (retested)
79
+ console.log(` ✓ ${retested} retest${retested === 1 ? "" : "s"} recorded`);
80
+ if (reconciliation.evidence.length)
81
+ console.log(` ${reconciliation.evidence.length} artifact(s) persisted`);
82
+ }
83
+ const trustRoomLines = trustRoomHumanLines(response);
84
+ if (trustRoomLines.length) {
85
+ console.log("");
86
+ for (const line of trustRoomLines)
87
+ console.log(line);
88
+ }
89
+ }
90
+ process.exit(EXIT.OK);
91
+ }
92
+ catch (err) {
93
+ if (isRetryableDevXRunError(err)) {
94
+ const message = errorMessage(err);
95
+ const outboxPath = writeDevXRunCompleteOutboxEntry(process.cwd(), {
96
+ runId,
97
+ contractVersion: DEVX_RUN_CONTRACT_VERSION,
98
+ result,
99
+ error: message,
100
+ });
101
+ if (json) {
102
+ printJson(jsonEnvelope("run complete", { run_id: runId, outcome: "queued", queued_at: outboxPath, error: message }));
103
+ }
104
+ else {
105
+ console.error(`zivis run complete: Cloud unreachable (${message})\nQueued for retry: ${outboxPath}\nRun \`zivis sync --flush\` once the issue is resolved.`);
106
+ }
107
+ process.exit(EXIT.CLOUD_UNAVAILABLE);
108
+ }
109
+ failCommand("run complete", err, json);
110
+ }
111
+ });
112
+ run
113
+ .command("report <runId>")
114
+ .description("post confirmed findings/evidence/observations against a pending run as you go, without completing it (ZIV-332)")
115
+ .requiredOption("--input <path>", "path to a JSON item-batch file ({ findings?, evidence?, observations? }), or '-' for stdin")
116
+ .option("--json", "emit JSON only on stdout")
117
+ .action(async (runId, opts) => {
118
+ const json = !!opts.json;
119
+ let items;
120
+ try {
121
+ items = await readInput(opts.input);
122
+ }
123
+ catch (err) {
124
+ failCommand("run report", err, json);
125
+ }
126
+ if (!items || typeof items !== "object" || Array.isArray(items)) {
127
+ failCommand("run report", new Error("--input must be a JSON object with findings[], evidence[], and/or observations[]"), json);
128
+ }
129
+ const config = resolveConfigFromBinding(process.cwd());
130
+ const client = new ApiClient(config);
131
+ try {
132
+ const response = await reportDevXRunItems(client, { runId, items: items });
133
+ const output = jsonEnvelope("run report", {
134
+ run_id: runId,
135
+ outcome: response.outcome,
136
+ findings: response.items.findings,
137
+ evidence: response.items.evidence,
138
+ observations: response.items.observations,
139
+ ...(response.rejectedObservations?.length ? { rejected_observations: response.rejectedObservations } : {}),
140
+ run_totals: response.runTotals,
141
+ });
142
+ if (json) {
143
+ printJson(output);
144
+ }
145
+ else {
146
+ const findings = response.items.findings;
147
+ const evidence = response.items.evidence;
148
+ const observations = response.items.observations;
149
+ const created = findings.filter((f) => f.action === "created").length;
150
+ const matched = findings.length - created;
151
+ const retest = findings.filter((f) => f.retestRecommendation !== null).length;
152
+ const withHistory = findings.filter((f) => f.dispositionHistory.length > 0).length;
153
+ const total = findings.length + evidence.length + observations.length;
154
+ console.log(`zivis run report: applied ${total} item${total === 1 ? "" : "s"} (run ${runId} stays pending)`);
155
+ if (created)
156
+ console.log(` + ${created} new finding${created === 1 ? "" : "s"}`);
157
+ if (matched)
158
+ console.log(` = ${matched} matched existing finding${matched === 1 ? "" : "s"}`);
159
+ if (retest)
160
+ console.log(` ↻ ${retest} retest recommendation${retest === 1 ? "" : "s"} — cite in retests[] on run complete`);
161
+ if (withHistory)
162
+ console.log(` ⚑ ${withHistory} finding${withHistory === 1 ? "" : "s"} with prior disposition history — surface to the human; never auto-apply`);
163
+ if (evidence.length)
164
+ console.log(` ${evidence.length} evidence item${evidence.length === 1 ? "" : "s"} persisted`);
165
+ if (observations.length)
166
+ console.log(` ${observations.length} observation${observations.length === 1 ? "" : "s"} recorded`);
167
+ if (response.rejectedObservations?.length)
168
+ console.log(` ! ${response.rejectedObservations.length} observation(s) rejected — see --json for reasons`);
169
+ console.log(` run totals so far: ${response.runTotals.findings} findings / ${response.runTotals.evidence} evidence / ${response.runTotals.observations} observations`);
170
+ }
171
+ process.exit(EXIT.OK);
172
+ }
173
+ catch (err) {
174
+ failCommand("run report", err, json);
175
+ }
176
+ });
177
+ run
178
+ .command("cancel <runId>")
179
+ .description("cancel a pending run")
180
+ .option("--reason <text>", "optional human-readable cancellation reason")
181
+ .option("--json", "emit JSON only on stdout")
182
+ .action(async (runId, opts) => {
183
+ const json = !!opts.json;
184
+ const config = resolveConfigFromBinding(process.cwd());
185
+ const client = new ApiClient(config);
186
+ try {
187
+ const response = await cancelDevXRunRemote(client, { runId, reason: opts.reason ?? null });
188
+ const output = jsonEnvelope("run cancel", {
189
+ run_id: runId,
190
+ outcome: response.outcome,
191
+ status: response.run.status,
192
+ cancelled_at: response.run.cancelledAt,
193
+ });
194
+ if (json) {
195
+ printJson(output);
196
+ }
197
+ else {
198
+ console.log(`zivis run cancel: ${response.outcome} (run ${runId} is now ${response.run.status})`);
199
+ }
200
+ process.exit(EXIT.OK);
201
+ }
202
+ catch (err) {
203
+ failCommand("run cancel", err, json);
204
+ }
205
+ });
206
+ }
@@ -1,8 +1,8 @@
1
1
  import { ApiClient } from "@zivis/mcp/api-client";
2
- import { DEFAULT_CONFIG } from "@zivis/mcp/types";
2
+ import { resolveConfigFromBinding } from "@zivis/mcp/project-binding";
3
3
  const VALID_TARGET_TYPES = ["ai_chat", "web_app", "api"];
4
4
  function buildClient(opts) {
5
- const config = opts.session ? { ...DEFAULT_CONFIG, session: opts.session } : DEFAULT_CONFIG;
5
+ const config = opts.session ? { ...resolveConfigFromBinding(process.cwd()), session: opts.session } : resolveConfigFromBinding(process.cwd());
6
6
  return new ApiClient(config);
7
7
  }
8
8
  export function registerScanCommands(program) {
@@ -118,11 +118,6 @@ export function registerScanCommands(program) {
118
118
  }
119
119
  });
120
120
  }
121
- /**
122
- * Map customer-facing ApplicationType to the RT scan's targetType enum.
123
- * The two enums differ historically: ApplicationType is product-shape,
124
- * targetType is what the RT runner needs to know about the wire format.
125
- */
126
121
  function appTypeToTargetType(appType) {
127
122
  switch (appType) {
128
123
  case "agent":
@@ -131,10 +126,10 @@ function appTypeToTargetType(appType) {
131
126
  return "ai_chat";
132
127
  case "rest_api":
133
128
  return "api";
129
+ case "application":
134
130
  case "ad_hoc_url":
135
131
  return "web_app";
136
132
  default:
137
133
  return undefined;
138
134
  }
139
135
  }
140
- //# sourceMappingURL=index.js.map
@@ -0,0 +1,14 @@
1
+ import type { Command } from "commander";
2
+ export interface ShareResponse {
3
+ trustRoomId: string;
4
+ trustRoomName: string;
5
+ trustRoomUrl: string;
6
+ accessUrl: string;
7
+ email: string;
8
+ permission: string;
9
+ status: "shared" | "already_shared";
10
+ isNewUser: boolean;
11
+ emailSent: boolean;
12
+ }
13
+ export declare function registerShareCommands(program: Command): void;
14
+ export default registerShareCommands;
@@ -0,0 +1,100 @@
1
+ import readline from "node:readline/promises";
2
+ import { ApiClient } from "@zivis/mcp/api-client";
3
+ import { resolveConfigFromBinding } from "@zivis/mcp/project-binding";
4
+ import { requireApplicationId } from "@zivis/mcp/resolve-application-id";
5
+ import { jsonEnvelope, printJson, failCommand, EXIT } from "../../internal/cli-output.js";
6
+ const COMMAND = "share";
7
+ const PERMISSIONS = ["view", "comment", "edit", "admin"];
8
+ async function prompt(question) {
9
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
10
+ try {
11
+ return (await rl.question(question)).trim();
12
+ }
13
+ finally {
14
+ rl.close();
15
+ }
16
+ }
17
+ function isValidEmail(value) {
18
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
19
+ }
20
+ export function registerShareCommands(program) {
21
+ program
22
+ .command("share")
23
+ .description("share this Application's current Assurance Room with someone by email")
24
+ .argument("[email]", "who to share with (prompted for when omitted)")
25
+ .option("-s, --session <name>", "named session for multi-tenant")
26
+ .option("--app <id>", "Application id (defaults to the bound Application)")
27
+ .option("--permission <level>", `access level: ${PERMISSIONS.join(" | ")}`, "view")
28
+ .option("--json", "emit machine-readable JSON")
29
+ .action(async (emailArg, opts) => {
30
+ const json = Boolean(opts.json);
31
+ try {
32
+ const permission = (opts.permission ?? "view").toLowerCase();
33
+ if (!PERMISSIONS.includes(permission)) {
34
+ throw new Error(`--permission must be one of: ${PERMISSIONS.join(", ")}`);
35
+ }
36
+ const resolvedApp = requireApplicationId(opts.app, process.cwd());
37
+ if (!resolvedApp.ok) {
38
+ if (json) {
39
+ printJson(jsonEnvelope(COMMAND, { result: "error", error: resolvedApp.message }));
40
+ }
41
+ else {
42
+ console.error(`zivis ${COMMAND}: ${resolvedApp.message}`);
43
+ }
44
+ process.exit(EXIT.INVALID_OR_INDETERMINATE);
45
+ }
46
+ let email = (emailArg ?? "").trim();
47
+ if (!email) {
48
+ if (json) {
49
+ throw new Error("An email argument is required with --json (no prompting).");
50
+ }
51
+ email = await prompt("Share this Assurance Room with (email): ");
52
+ }
53
+ if (!isValidEmail(email)) {
54
+ throw new Error(`'${email}' does not look like an email address.`);
55
+ }
56
+ const config = opts.session
57
+ ? { ...resolveConfigFromBinding(process.cwd()), session: opts.session }
58
+ : resolveConfigFromBinding(process.cwd());
59
+ const client = new ApiClient(config);
60
+ const res = await client.post("/api/assurance/share", {
61
+ applicationId: resolvedApp.id,
62
+ email,
63
+ permission,
64
+ });
65
+ if (json) {
66
+ printJson(jsonEnvelope(COMMAND, {
67
+ result: "ok",
68
+ status: res.status,
69
+ email: res.email,
70
+ permission: res.permission,
71
+ is_new_user: res.isNewUser,
72
+ email_sent: res.emailSent,
73
+ trust_room_id: res.trustRoomId,
74
+ trust_room_url: res.trustRoomUrl,
75
+ access_url: res.accessUrl,
76
+ }));
77
+ return;
78
+ }
79
+ if (res.status === "already_shared") {
80
+ console.log(`${res.email} already has access to ${res.trustRoomName}.`);
81
+ }
82
+ else {
83
+ console.log(`Shared ${res.trustRoomName} with ${res.email} (${res.permission}).`);
84
+ }
85
+ if (res.isNewUser) {
86
+ console.log(`${res.email} has no ZIVIS account yet — they'll create one when they open the link.`);
87
+ }
88
+ if (!res.emailSent) {
89
+ console.log("Access was granted, but the invitation email could not be sent.");
90
+ console.log("Pass this link on directly:");
91
+ }
92
+ console.log("");
93
+ console.log(`Assurance Room: ${res.accessUrl}`);
94
+ }
95
+ catch (err) {
96
+ failCommand(COMMAND, err, json);
97
+ }
98
+ });
99
+ }
100
+ export default registerShareCommands;
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerSyncCommands(program: Command): void;
@@ -0,0 +1,193 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import { ApiClient } from "@zivis/mcp/api-client";
4
+ import { detectProjectBinding, resolveConfigFromBinding } from "@zivis/mcp/project-binding";
5
+ import { readGitMetadata, deriveRepoFullNameFromRemote } from "../../internal/git-metadata.js";
6
+ import { validateInventorySyncInput, syncApplicationInventory, } from "../../internal/inventory-sync.js";
7
+ import { ensureZivisGitignore } from "../../internal/zivis-local-state.js";
8
+ import { writeOutboxEntry, listOutboxEntries, removeOutboxEntry, recordOutboxRetryFailure, } from "../../internal/sync-outbox.js";
9
+ import { completeDevXRunRemote } from "../../internal/devx-run.js";
10
+ export function registerSyncCommands(program) {
11
+ program
12
+ .command("sync")
13
+ .description("push a locally-derived endpoint/feature/technology-stack inventory to your bound ZIVIS Application " +
14
+ "(the coding agent derives the inventory; this command reconciles it — see --file)")
15
+ .option("--file <path>", "JSON file with { endpoints?, features?, technologyStack? } (use - for stdin)")
16
+ .option("--flush", "retry pending outbox entries from a previous failed sync, instead of reading --file")
17
+ .option("--cwd <path>", "repo root (default: cwd)")
18
+ .option("--source <source>", "provenance source: cli or agent", "agent")
19
+ .option("--format <fmt>", "output format: human or json", "human")
20
+ .option("--dry-run", "validate/list and print what would be sent — no network calls")
21
+ .action(async (opts) => {
22
+ const exitCode = opts.flush ? await runFlush(opts) : await runSync(opts);
23
+ process.exit(exitCode);
24
+ });
25
+ }
26
+ async function runSync(opts) {
27
+ const rootDir = opts.cwd ? path.resolve(opts.cwd) : process.cwd();
28
+ const format = opts.format === "json" ? "json" : "human";
29
+ const source = opts.source === "cli" ? "cli" : "agent";
30
+ if (!opts.file) {
31
+ return fail(format, "--file is required (or pass --flush to retry queued entries)");
32
+ }
33
+ if (opts.source && opts.source !== "cli" && opts.source !== "agent") {
34
+ return fail(format, `--source must be "cli" or "agent" (got: ${opts.source})`);
35
+ }
36
+ let raw;
37
+ try {
38
+ raw = opts.file === "-" ? await readStdin() : await fs.readFile(path.resolve(rootDir, opts.file), "utf-8");
39
+ }
40
+ catch (err) {
41
+ return fail(format, `Could not read ${opts.file}: ${err instanceof Error ? err.message : err}`);
42
+ }
43
+ let parsed;
44
+ try {
45
+ parsed = JSON.parse(raw);
46
+ }
47
+ catch (err) {
48
+ return fail(format, `Invalid JSON: ${err instanceof Error ? err.message : err}`);
49
+ }
50
+ const validated = validateInventorySyncInput(parsed);
51
+ if (!validated.ok) {
52
+ return fail(format, `Invalid inventory input:\n - ${validated.errors.join("\n - ")}`);
53
+ }
54
+ const binding = detectProjectBinding(rootDir);
55
+ const applicationId = binding?.binding.applicationId;
56
+ if (!applicationId) {
57
+ return fail(format, "No Application bound for this repo. Run `zivis init` first (see .zivis/project.json).");
58
+ }
59
+ const gitMeta = await readGitMetadata(rootDir);
60
+ const repoFullName = deriveRepoFullNameFromRemote(gitMeta.remoteUrl);
61
+ const provenance = {
62
+ gitCommitSha: gitMeta.commitSha ?? null,
63
+ repoFullName: repoFullName ?? null,
64
+ defaultBranch: gitMeta.branch ?? null,
65
+ source,
66
+ };
67
+ if (opts.dryRun) {
68
+ process.stdout.write(JSON.stringify({ applicationId, provenance, input: validated.value }, null, 2) + "\n");
69
+ if (format === "human") {
70
+ process.stderr.write("\n[zivis sync] --dry-run: nothing was sent.\n");
71
+ }
72
+ return 0;
73
+ }
74
+ ensureZivisGitignore(rootDir);
75
+ const config = resolveConfigFromBinding(rootDir);
76
+ const client = new ApiClient(config);
77
+ let summary;
78
+ try {
79
+ summary = await syncApplicationInventory({ client, applicationId, input: validated.value, provenance });
80
+ }
81
+ catch (err) {
82
+ const message = err instanceof Error ? err.message : String(err);
83
+ const outboxPath = writeOutboxEntry(rootDir, { applicationId, input: validated.value, provenance, error: message });
84
+ return fail(format, `Sync failed: ${message}\nQueued for retry: ${outboxPath}\nRun \`zivis sync --flush\` once the issue is resolved.`);
85
+ }
86
+ if (format === "json") {
87
+ process.stdout.write(JSON.stringify({ applicationId, summary }, null, 2) + "\n");
88
+ return 0;
89
+ }
90
+ renderHumanSummary(summary);
91
+ return 0;
92
+ }
93
+ async function runFlush(opts) {
94
+ const rootDir = opts.cwd ? path.resolve(opts.cwd) : process.cwd();
95
+ const format = opts.format === "json" ? "json" : "human";
96
+ const pending = listOutboxEntries(rootDir);
97
+ if (pending.length === 0) {
98
+ if (format === "json")
99
+ process.stdout.write(JSON.stringify({ pending: 0 }, null, 2) + "\n");
100
+ else
101
+ process.stdout.write("\nNo pending outbox entries.\n");
102
+ return 0;
103
+ }
104
+ if (opts.dryRun) {
105
+ const preview = pending.map((p) => ({
106
+ file: p.filePath,
107
+ kind: p.entry.kind,
108
+ queuedAt: p.entry.queuedAt,
109
+ attempts: p.entry.attempts,
110
+ lastError: p.entry.lastError,
111
+ ...(p.entry.kind === "devx-run-complete"
112
+ ? { runId: p.entry.runId, result: p.entry.result }
113
+ : { applicationId: p.entry.applicationId, input: p.entry.input }),
114
+ }));
115
+ process.stdout.write(JSON.stringify(preview, null, 2) + "\n");
116
+ if (format === "human")
117
+ process.stderr.write(`\n[zivis sync --flush] --dry-run: ${pending.length} pending, nothing sent.\n`);
118
+ return 0;
119
+ }
120
+ const config = resolveConfigFromBinding(rootDir);
121
+ const client = new ApiClient(config);
122
+ let succeeded = 0;
123
+ let failed = 0;
124
+ const trustRoomUrls = [];
125
+ for (const { filePath, entry } of pending) {
126
+ try {
127
+ if (entry.kind === "devx-run-complete") {
128
+ const completed = await completeDevXRunRemote(client, { runId: entry.runId, contractVersion: entry.contractVersion, result: entry.result });
129
+ if (completed.trustRoomUrl && !trustRoomUrls.includes(completed.trustRoomUrl)) {
130
+ trustRoomUrls.push(completed.trustRoomUrl);
131
+ }
132
+ }
133
+ else {
134
+ await syncApplicationInventory({
135
+ client,
136
+ applicationId: entry.applicationId,
137
+ input: entry.input,
138
+ provenance: entry.provenance,
139
+ });
140
+ }
141
+ removeOutboxEntry(filePath);
142
+ succeeded++;
143
+ }
144
+ catch (err) {
145
+ recordOutboxRetryFailure(filePath, entry, err instanceof Error ? err.message : String(err));
146
+ failed++;
147
+ }
148
+ }
149
+ if (format === "json") {
150
+ process.stdout.write(JSON.stringify({ succeeded, failed, remaining: failed, ...(trustRoomUrls.length ? { trust_room_urls: trustRoomUrls } : {}) }, null, 2) + "\n");
151
+ }
152
+ else {
153
+ process.stdout.write(`\nFlushed outbox: ${succeeded} succeeded, ${failed} still failing (left queued).\n`);
154
+ for (const url of trustRoomUrls)
155
+ process.stdout.write(`Trust Room: ${url}\n`);
156
+ }
157
+ return failed > 0 ? 1 : 0;
158
+ }
159
+ function renderHumanSummary(summary) {
160
+ let out = "\nZIVIS inventory sync:\n\n";
161
+ if (summary.technologyStack) {
162
+ out += ` Technology stack: ${summary.technologyStack.count} entries\n`;
163
+ }
164
+ if (summary.endpoints) {
165
+ const e = summary.endpoints;
166
+ out += ` Endpoints: +${e.created} new, ${e.updated} updated, ${e.deprecated} deprecated (${e.total} total reported)\n`;
167
+ }
168
+ if (summary.features) {
169
+ const f = summary.features;
170
+ out += ` Features: +${f.created} new, ${f.updated} updated, ${f.deprecated} deprecated\n`;
171
+ }
172
+ if (summary.repo) {
173
+ out += ` Repository: ${summary.repo.repoFullName} (${summary.repo.created ? "linked" : "revision updated"})\n`;
174
+ }
175
+ process.stdout.write(out + "\n");
176
+ }
177
+ function fail(format, message) {
178
+ if (format === "json") {
179
+ process.stdout.write(JSON.stringify({ error: message }, null, 2) + "\n");
180
+ }
181
+ else {
182
+ process.stderr.write(`\n${message}\n`);
183
+ }
184
+ return 1;
185
+ }
186
+ async function readStdin() {
187
+ let data = "";
188
+ process.stdin.setEncoding("utf-8");
189
+ for await (const chunk of process.stdin) {
190
+ data += chunk;
191
+ }
192
+ return data;
193
+ }
@@ -4,8 +4,6 @@ import { getStorageName } from "../../internal/target-auth/keychain.js";
4
4
  import { decodeJwtPayload } from "../../internal/target-auth/workos.js";
5
5
  const DEFAULT_PERSONA = "default";
6
6
  function warnDeprecated() {
7
- // stderr so it doesn't pollute stdout (CLI scripts pipe `zivis target token`
8
- // straight into curl). One line, no color codes — keeps grep-friendly.
9
7
  console.error("[zivis] WARNING: 'zivis target' is deprecated; use 'zivis app' instead.");
10
8
  }
11
9
  export function registerTargetCommands(program) {
@@ -143,4 +141,3 @@ export function registerTargetCommands(program) {
143
141
  console.log(`✓ Cleared tokens for target ${targetId} (persona: ${opts.persona})`);
144
142
  });
145
143
  }
146
- //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerTestCommand(program: Command): void;