orizu 0.5.52 → 0.5.54

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 (80) hide show
  1. package/dist/app-commands.d.ts +2 -1
  2. package/dist/app-commands.d.ts.map +1 -1
  3. package/dist/app-commands.js +27 -20
  4. package/dist/auth-commands.js +2 -2
  5. package/dist/cli-console-output.d.ts +7 -1
  6. package/dist/cli-console-output.d.ts.map +1 -1
  7. package/dist/cli-console-output.js +16 -3
  8. package/dist/cli-stops.d.ts +14 -0
  9. package/dist/cli-stops.d.ts.map +1 -0
  10. package/dist/cli-stops.js +22 -0
  11. package/dist/egress-allowlist-cli.d.ts +29 -2
  12. package/dist/egress-allowlist-cli.d.ts.map +1 -1
  13. package/dist/egress-allowlist-cli.js +16 -89
  14. package/dist/egress-allowlist-workflow.d.ts +17 -0
  15. package/dist/egress-allowlist-workflow.d.ts.map +1 -0
  16. package/dist/egress-allowlist-workflow.js +183 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +7 -1
  19. package/dist/instruction-delivery.d.ts +4 -4
  20. package/dist/instruction-delivery.d.ts.map +1 -1
  21. package/dist/instruction-delivery.js +32 -46
  22. package/dist/instruction-prune.d.ts +24 -0
  23. package/dist/instruction-prune.d.ts.map +1 -0
  24. package/dist/instruction-prune.js +33 -0
  25. package/dist/instruction-set-sync/index.d.ts +0 -2
  26. package/dist/instruction-set-sync/index.d.ts.map +1 -1
  27. package/dist/instruction-set-sync/index.js +3 -36
  28. package/dist/instruction-set-update/index.d.ts +41 -3
  29. package/dist/instruction-set-update/index.d.ts.map +1 -1
  30. package/dist/instruction-set-update/index.js +156 -132
  31. package/dist/instruction-sets-cli.d.ts.map +1 -1
  32. package/dist/instruction-sets-cli.js +21 -2
  33. package/dist/instruction-sync-lock.d.ts +9 -0
  34. package/dist/instruction-sync-lock.d.ts.map +1 -0
  35. package/dist/instruction-sync-lock.js +24 -0
  36. package/dist/json-output-scrub.d.ts +3 -0
  37. package/dist/json-output-scrub.d.ts.map +1 -0
  38. package/dist/json-output-scrub.js +70 -0
  39. package/dist/one-request-write.d.ts.map +1 -1
  40. package/dist/one-request-write.js +2 -9
  41. package/dist/preview-runtime.d.ts +14 -3
  42. package/dist/preview-runtime.d.ts.map +1 -1
  43. package/dist/preview-runtime.js +13 -255
  44. package/dist/preview-workflow.d.ts +11 -0
  45. package/dist/preview-workflow.d.ts.map +1 -0
  46. package/dist/preview-workflow.js +260 -0
  47. package/dist/workbench-cli.d.ts +2 -1
  48. package/dist/workbench-cli.d.ts.map +1 -1
  49. package/dist/workbench-cli.js +79 -593
  50. package/dist/workbench-workflows.d.ts +56 -0
  51. package/dist/workbench-workflows.d.ts.map +1 -0
  52. package/dist/workbench-workflows.js +665 -0
  53. package/dist/workspace-sync-workflow.d.ts +115 -0
  54. package/dist/workspace-sync-workflow.d.ts.map +1 -0
  55. package/dist/workspace-sync-workflow.js +642 -0
  56. package/dist/workspace-sync.d.ts +45 -97
  57. package/dist/workspace-sync.d.ts.map +1 -1
  58. package/dist/workspace-sync.js +53 -494
  59. package/package.json +1 -1
  60. package/src/app-commands.ts +30 -22
  61. package/src/auth-commands.ts +2 -2
  62. package/src/cli-console-output.ts +16 -2
  63. package/src/cli-stops.ts +27 -0
  64. package/src/egress-allowlist-cli.ts +18 -90
  65. package/src/egress-allowlist-workflow.ts +198 -0
  66. package/src/index.ts +7 -1
  67. package/src/instruction-delivery.ts +31 -47
  68. package/src/instruction-prune.ts +59 -0
  69. package/src/instruction-set-sync/index.ts +3 -43
  70. package/src/instruction-set-update/index.ts +173 -122
  71. package/src/instruction-sets-cli.ts +23 -2
  72. package/src/instruction-sync-lock.ts +27 -0
  73. package/src/json-output-scrub.ts +73 -0
  74. package/src/one-request-write.ts +2 -10
  75. package/src/preview-runtime.ts +18 -280
  76. package/src/preview-workflow.ts +313 -0
  77. package/src/workbench-cli.ts +83 -684
  78. package/src/workbench-workflows.ts +815 -0
  79. package/src/workspace-sync-workflow.ts +838 -0
  80. package/src/workspace-sync.ts +63 -630
@@ -20,7 +20,8 @@ declare function readPromptSidecars(manifest: Record<string, unknown>, promptDir
20
20
  declare function writeTextFileEnsuringDir(pathArg: string, content: string): void;
21
21
  declare function readManifestFile(dirArg: string): Record<string, unknown>;
22
22
  declare function createAppFromFile(): Promise<void>;
23
- declare function previewAppFromFile(): Promise<void>;
23
+ /** Resolves to the exit code of a stop (130 or 143), or undefined when the preview ended normally. */
24
+ declare function previewAppFromFile(): Promise<number | undefined>;
24
25
  declare function updateAppFromFile(): Promise<void>;
25
26
  declare function linkAppDataset(): Promise<void>;
26
27
  declare function appDetail(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"app-commands.d.ts","sourceRoot":"","sources":["../src/app-commands.ts"],"names":[],"mappings":"AAuCA,iBAAe,QAAQ,kBActB;AAED,iBAAS,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAU/C;AAED,iBAAS,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAUhD;AAED,iBAAS,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE9D;AAeD,iBAAS,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAElE;AAED,iBAAS,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG1F;AAED,iBAAS,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUjE;AAED,iBAAS,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAEjD;AAED,iBAAS,qBAAqB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG;IACpF,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb,CAeA;AAED,iBAAS,kBAAkB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM;;;;;IAoB/E;AAED,iBAAS,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,QAGjE;AAED,iBAAS,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEjE;AAyED,iBAAe,iBAAiB,kBAmG/B;AAED,iBAAe,kBAAkB,kBAiDhC;AAED,iBAAe,iBAAiB,kBAwF/B;AAED,iBAAe,cAAc,kBAiC5B;AA+CD,iBAAe,SAAS,kBA2CvB;AAED,yFAAyF;AACzF,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAK3G;AAED,iBAAe,eAAe,kBAmD7B;AAED,OAAO,EACL,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACR,gBAAgB,EAChB,wBAAwB,EACxB,SAAS,EACT,eAAe,EACf,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,eAAe,GAChB,CAAA"}
1
+ {"version":3,"file":"app-commands.d.ts","sourceRoot":"","sources":["../src/app-commands.ts"],"names":[],"mappings":"AAuCA,iBAAe,QAAQ,kBActB;AAED,iBAAS,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAU/C;AAED,iBAAS,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAUhD;AAED,iBAAS,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE9D;AAeD,iBAAS,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAElE;AAED,iBAAS,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG1F;AAED,iBAAS,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUjE;AAED,iBAAS,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAEjD;AAED,iBAAS,qBAAqB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG;IACpF,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb,CAeA;AAED,iBAAS,kBAAkB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM;;;;;IAoB/E;AAED,iBAAS,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,QAGjE;AAED,iBAAS,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEjE;AAyED,iBAAe,iBAAiB,kBAmG/B;AAED,sGAAsG;AACtG,iBAAe,kBAAkB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAwD/D;AAED,iBAAe,iBAAiB,kBAwF/B;AAED,iBAAe,cAAc,kBAiC5B;AA+CD,iBAAe,SAAS,kBA2CvB;AAED,yFAAyF;AACzF,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAK3G;AAED,iBAAe,eAAe,kBAmD7B;AAED,OAAO,EACL,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACR,gBAAgB,EAChB,wBAAwB,EACxB,SAAS,EACT,eAAe,EACf,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,eAAe,GAChB,CAAA"}
@@ -13,7 +13,6 @@ import { inspectAppDestination } from './app-publish-paths.js';
13
13
  import { authedFetch, getBaseUrl } from './http.js';
14
14
  import { appPageUrl } from './app-page-url.js';
15
15
  import { parseJsonResponse, sanitizeHumanInlineText, sanitizeTerminalText } from './json-response.js';
16
- import { runLocalAppPreview } from './preview-runtime.js';
17
16
  import { findWorkspaceRoot } from './workspace.js';
18
17
  async function listApps() {
19
18
  const project = getArg('--project') || await resolveProjectSlug(null);
@@ -289,6 +288,7 @@ async function createAppFromFile() {
289
288
  throw new Error(`${getErrorMessage(error)}\nApp ID: ${appId}\nFolder: ${recoveryFolder}\nRetry: ${retryArgs.map(shellSingleQuote).join(' ')}`);
290
289
  }
291
290
  }
291
+ /** Resolves to the exit code of a stop (130 or 143), or undefined when the preview ended normally. */
292
292
  async function previewAppFromFile() {
293
293
  const filePath = getArg('--file');
294
294
  const inputSchemaPath = getArg('--input-schema');
@@ -306,7 +306,29 @@ async function previewAppFromFile() {
306
306
  const outputJsonSchema = readJsonFile(outputSchemaPath);
307
307
  const sampleRow = readJsonFile(sampleRowPath);
308
308
  const expandedScreenshotPath = screenshotPath ? expandHomePath(screenshotPath) : undefined;
309
- const result = await runLocalAppPreview({
309
+ const printRendered = (result) => {
310
+ if (hasJsonFlag()) {
311
+ printJson({
312
+ url: result.url,
313
+ screenshotPath: result.screenshotPath || null,
314
+ warnings: result.warnings,
315
+ });
316
+ return;
317
+ }
318
+ printLine(`Preview rendered: ${formatTerminalLink(result.url)}`);
319
+ if (result.screenshotPath) {
320
+ printLine(`Screenshot: ${sanitizeTerminalText(result.screenshotPath)}`);
321
+ }
322
+ if (headed || keepOpen) {
323
+ printLine('Headed preview is running. Close Chromium or stop the command when you are done.');
324
+ }
325
+ if (result.warnings.length > 0) {
326
+ printLine(`Warnings: ${sanitizeTerminalText(result.warnings.join('; '))}`);
327
+ }
328
+ };
329
+ const { runPreviewCommand } = await import('./preview-workflow.js');
330
+ // Printed once the page is ready, so a keep-open preview says so while it is open.
331
+ const outcome = await runPreviewCommand({
310
332
  filePath: expandedFilePath,
311
333
  inputSchema: inputJsonSchema,
312
334
  outputSchema: outputJsonSchema,
@@ -315,25 +337,10 @@ async function previewAppFromFile() {
315
337
  componentName: component,
316
338
  headed,
317
339
  keepOpen,
340
+ onRendered: printRendered,
318
341
  });
319
- if (hasJsonFlag()) {
320
- printJson({
321
- url: result.url,
322
- screenshotPath: result.screenshotPath || null,
323
- warnings: result.warnings,
324
- });
325
- return;
326
- }
327
- printLine(`Preview rendered: ${formatTerminalLink(result.url)}`);
328
- if (result.screenshotPath) {
329
- printLine(`Screenshot: ${sanitizeTerminalText(result.screenshotPath)}`);
330
- }
331
- if (headed || keepOpen) {
332
- printLine('Headed preview is running. Close Chromium or stop the command when you are done.');
333
- }
334
- if (result.warnings.length > 0) {
335
- printLine(`Warnings: ${sanitizeTerminalText(result.warnings.join('; '))}`);
336
- }
342
+ // A stop prints nothing more.
343
+ return 'interrupted' in outcome ? outcome.interrupted : undefined;
337
344
  }
338
345
  async function updateAppFromFile() {
339
346
  const workspaceRoot = hostedWorkspaceRoot();
@@ -4,7 +4,7 @@ import { sanitizeTerminalText, parseJsonResponse } from './json-response.js';
4
4
  import { isInteractiveTerminal, hasArg, hasJsonFlag, getArg } from './command-line-arguments.js';
5
5
  import { getBaseUrl, resolveLoginTarget, resolveLogoutTarget, assertSecureTokenTransport, authedFetch, } from './http.js';
6
6
  import { clearUnreadableServerCredentials, hasResolvableAuth, rememberProcessSecret, saveServerCredentials, getServerCredentials, resolveAuthTokenForBaseUrl, } from './credentials.js';
7
- import { printJson, printLine, printLoginProgress } from './cli-console-output.js';
7
+ import { printJson, printJsonWithHeldSecrets, printLine, printLoginProgress } from './cli-console-output.js';
8
8
  import { resolveProjectSelection } from './account-directory.js';
9
9
  function isNodeError(error) {
10
10
  return error instanceof Error;
@@ -196,7 +196,7 @@ async function printEnv() {
196
196
  const projectId = project?.id || getArg('--project-id') || process.env.ORIZU_PROJECT_ID || '';
197
197
  const projectSlug = project ? `${project.teamSlug}/${project.slug}` : process.env.ORIZU_PROJECT || '';
198
198
  if (hasJsonFlag()) {
199
- printJson({
199
+ printJsonWithHeldSecrets({
200
200
  ORIZU_API_URL: baseUrl,
201
201
  ORIZU_TOKEN: token,
202
202
  ORIZU_PROJECT_ID: projectId,
@@ -8,5 +8,11 @@ declare function printError(message: string): void;
8
8
  declare function printLoginProgress(message: string): void;
9
9
  declare function printPushDisclosures(data: Record<string, unknown>): void;
10
10
  declare function printJson(value: Record<string, unknown>): void;
11
- export { getErrorMessage, printJson, printLine, printPushDisclosures, getCliVersion, printBannerIfInteractive, printVersion, printUsage, printError, printLoginProgress, };
11
+ /**
12
+ * The one --json output allowed to carry a held secret: `orizu env --json`
13
+ * prints the token so a script can export it. Any new caller needs its own
14
+ * reviewed change and a case in cli-json-output-scrub-process.test.ts.
15
+ */
16
+ declare function printJsonWithHeldSecrets(value: Record<string, unknown>): void;
17
+ export { getErrorMessage, printJson, printJsonWithHeldSecrets, printLine, printPushDisclosures, getCliVersion, printBannerIfInteractive, printVersion, printUsage, printError, printLoginProgress, };
12
18
  //# sourceMappingURL=cli-console-output.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-console-output.d.ts","sourceRoot":"","sources":["../src/cli-console-output.ts"],"names":[],"mappings":"AAUA,iBAAS,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE/C;AAED,iBAAS,aAAa,IAAI,MAAM,CAU/B;AAED,iBAAS,SAAS,CAAC,OAAO,SAAK,QAE9B;AAED,iBAAS,YAAY,SAEpB;AAED,iBAAS,wBAAwB,SAMhC;AAED,iBAAS,UAAU,SAGlB;AAED,iBAAS,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEzC;AAED,iBAAS,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAMjD;AAED,iBAAS,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAMjE;AAED,iBAAS,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAEhD;AAED,OAAO,EACL,eAAe,EACf,SAAS,EACT,SAAS,EACT,oBAAoB,EACpB,aAAa,EACb,wBAAwB,EACxB,YAAY,EACZ,UAAU,EACV,UAAU,EACV,kBAAkB,GACnB,CAAA"}
1
+ {"version":3,"file":"cli-console-output.d.ts","sourceRoot":"","sources":["../src/cli-console-output.ts"],"names":[],"mappings":"AAWA,iBAAS,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE/C;AAED,iBAAS,aAAa,IAAI,MAAM,CAU/B;AAGD,iBAAS,SAAS,CAAC,OAAO,SAAK,QAE9B;AAED,iBAAS,YAAY,SAEpB;AAED,iBAAS,wBAAwB,SAMhC;AAED,iBAAS,UAAU,SAGlB;AAED,iBAAS,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEzC;AAED,iBAAS,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAMjD;AAED,iBAAS,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAMjE;AAED,iBAAS,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAEhD;AAED;;;;GAIG;AACH,iBAAS,wBAAwB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAI/D;AAED,OAAO,EACL,eAAe,EACf,SAAS,EACT,wBAAwB,EACxB,SAAS,EACT,oBAAoB,EACpB,aAAa,EACb,wBAAwB,EACxB,YAAY,EACZ,UAAU,EACV,UAAU,EACV,kBAAkB,GACnB,CAAA"}
@@ -4,7 +4,8 @@ import { stdout as output } from 'process';
4
4
  import { renderBanner } from './banner.js';
5
5
  import { renderRootHelp } from './help.js';
6
6
  import { sanitizeTerminalText } from './json-response.js';
7
- import { hasJsonFlag } from './command-line-arguments.js';
7
+ import { cliArgs, hasJsonFlag } from './command-line-arguments.js';
8
+ import { hideHeldSecrets } from './json-output-scrub.js';
8
9
  import { reportReplacementWarning } from './report-replacement-warning.js';
9
10
  function getErrorMessage(error) {
10
11
  return error instanceof Error ? error.message : String(error);
@@ -16,8 +17,9 @@ function getCliVersion() {
16
17
  }
17
18
  return packageJson.version;
18
19
  }
20
+ // ORI-2744: under --json, stdout never carries a secret this process holds.
19
21
  function printLine(message = '') {
20
- output.write(`${message}\n`);
22
+ output.write(`${hasJsonFlag() ? hideHeldSecrets(message) : message}\n`);
21
23
  }
22
24
  function printVersion() {
23
25
  printLine(`orizu ${getCliVersion()}`);
@@ -54,4 +56,15 @@ function printPushDisclosures(data) {
54
56
  function printJson(value) {
55
57
  printLine(JSON.stringify(value));
56
58
  }
57
- export { getErrorMessage, printJson, printLine, printPushDisclosures, getCliVersion, printBannerIfInteractive, printVersion, printUsage, printError, printLoginProgress, };
59
+ /**
60
+ * The one --json output allowed to carry a held secret: `orizu env --json`
61
+ * prints the token so a script can export it. Any new caller needs its own
62
+ * reviewed change and a case in cli-json-output-scrub-process.test.ts.
63
+ */
64
+ function printJsonWithHeldSecrets(value) {
65
+ // Any other command is scrubbed as usual, so a wrong caller can't leak.
66
+ if (cliArgs[0] !== 'env')
67
+ return printJson(value);
68
+ output.write(`${JSON.stringify(value)}\n`);
69
+ }
70
+ export { getErrorMessage, printJson, printJsonWithHeldSecrets, printLine, printPushDisclosures, getCliVersion, printBannerIfInteractive, printVersion, printUsage, printError, printLoginProgress, };
@@ -0,0 +1,14 @@
1
+ import { Effect } from 'effect';
2
+ export interface Interrupted {
3
+ interrupted: 130 | 143;
4
+ }
5
+ /**
6
+ * Runs the program with Ctrl-C and SIGTERM turned into an interrupt, and says
7
+ * which stop arrived, if any. `once`: a second signal falls through to Node's
8
+ * default kill. The handlers are removed before this returns.
9
+ */
10
+ export declare function runWithStops<A>(program: (signal: AbortSignal) => Effect.Effect<A, unknown>): Promise<{
11
+ exit: import("effect/Exit").Exit<A, unknown>;
12
+ interrupted: 130 | 143 | undefined;
13
+ }>;
14
+ //# sourceMappingURL=cli-stops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-stops.d.ts","sourceRoot":"","sources":["../src/cli-stops.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAK/B,MAAM,WAAW,WAAW;IAAG,WAAW,EAAE,GAAG,GAAG,GAAG,CAAA;CAAE;AAEvD;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC;;;GAchG"}
@@ -0,0 +1,22 @@
1
+ import { Effect } from 'effect';
2
+ /**
3
+ * Runs the program with Ctrl-C and SIGTERM turned into an interrupt, and says
4
+ * which stop arrived, if any. `once`: a second signal falls through to Node's
5
+ * default kill. The handlers are removed before this returns.
6
+ */
7
+ export async function runWithStops(program) {
8
+ const controller = new AbortController();
9
+ let interrupted;
10
+ const handleInterrupt = () => { interrupted ??= 130; controller.abort(); };
11
+ const handleTerminate = () => { interrupted ??= 143; controller.abort(); };
12
+ process.once('SIGINT', handleInterrupt);
13
+ process.once('SIGTERM', handleTerminate);
14
+ try {
15
+ const exit = await Effect.runPromiseExit(program(controller.signal), { signal: controller.signal });
16
+ return { exit, interrupted };
17
+ }
18
+ finally {
19
+ process.removeListener('SIGINT', handleInterrupt);
20
+ process.removeListener('SIGTERM', handleTerminate);
21
+ }
22
+ }
@@ -9,15 +9,42 @@
9
9
  * The BASE allowlist (Orizu API + model provider + git host) is code-owned and
10
10
  * NOT shown/managed here — this surface only widens a team's egress with domains
11
11
  * a customer workflow needs. Human ADMIN only: the server rejects agent bearers
12
- * and non-admins (the definer RPC gates on is_team_admin). Pure command logic
13
- * with an injected fetcher.
12
+ * and non-admins (the definer RPC gates on is_team_admin). The arguments and
13
+ * output live here; the requests run on Effect in egress-allowlist-workflow.ts
14
+ * (ORI-2764), loaded only when this command runs.
14
15
  */
15
16
  export type CliFetcher = (path: string, init?: RequestInit) => Promise<Response>;
16
17
  export interface EgressAllowlistCommandIo {
17
18
  json: boolean;
18
19
  print: (line: string) => void;
19
20
  printErr?: (line: string) => void;
21
+ /** Stands in for `authedFetch` in tests. */
20
22
  fetcher?: CliFetcher;
21
23
  }
24
+ export declare function splitDomains(value: string): string[];
25
+ export interface CurrentAllowlist {
26
+ domains: string[];
27
+ /** ALI-1174: legacy entries the server flags as failing the tightened
28
+ * validator — stored, shown by GET, but silently dropped by the
29
+ * coordinator at policy compose. Surfaced so "configured" never silently
30
+ * means "inert". */
31
+ invalid: string[];
32
+ /** Valid entries beyond the 100-domain cap — equally inert at compose. */
33
+ capped: string[];
34
+ }
35
+ export declare function stringArray(value: unknown): string[];
36
+ /**
37
+ * Re-derive the "beyond the 100-domain cap" entries from a stored set the
38
+ * SAME way the GET route does (positional over UNIQUE VALID domains): the
39
+ * coordinator applies only the first MAX_ADDITIVE_EGRESS_DOMAINS valid
40
+ * domains, so every valid entry past that index is configured-but-inert.
41
+ * Stored entries are already canonical + deduped by the write RPC, and the
42
+ * invalid (grandfathered) ones — flagged by the server — are not "valid"
43
+ * and so never consume a cap slot. Used for the PUT response, where a
44
+ * shrink-by-one on an oversized legacy row leaves the returned set still
45
+ * over-cap and the server does not send cappedDomains back.
46
+ */
47
+ export declare function deriveCapped(stored: readonly string[], invalid: readonly string[]): string[];
48
+ export declare function emit(io: EgressAllowlistCommandIo, teamSlug: string, current: CurrentAllowlist): void;
22
49
  export declare function egressAllowlistCommand(args: string[], io: EgressAllowlistCommandIo): Promise<number>;
23
50
  //# sourceMappingURL=egress-allowlist-cli.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"egress-allowlist-cli.d.ts","sourceRoot":"","sources":["../src/egress-allowlist-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAWH,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEhF,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,OAAO,CAAC,EAAE,UAAU,CAAA;CACrB;AAiID,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CA4D1G"}
1
+ {"version":3,"file":"egress-allowlist-cli.d.ts","sourceRoot":"","sources":["../src/egress-allowlist-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AASH,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEhF,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,4CAA4C;IAC5C,OAAO,CAAC,EAAE,UAAU,CAAA;CACrB;AA0BD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAKpD;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB;;;yBAGqB;IACrB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAEpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAU5F;AAED,wBAAgB,IAAI,CAAC,EAAE,EAAE,wBAAwB,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI,CA6BpG;AAED,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAe1G"}
@@ -9,19 +9,16 @@
9
9
  * The BASE allowlist (Orizu API + model provider + git host) is code-owned and
10
10
  * NOT shown/managed here — this surface only widens a team's egress with domains
11
11
  * a customer workflow needs. Human ADMIN only: the server rejects agent bearers
12
- * and non-admins (the definer RPC gates on is_team_admin). Pure command logic
13
- * with an injected fetcher.
12
+ * and non-admins (the definer RPC gates on is_team_admin). The arguments and
13
+ * output live here; the requests run on Effect in egress-allowlist-workflow.ts
14
+ * (ORI-2764), loaded only when this command runs.
14
15
  */
15
- import { authedFetch } from './http.js';
16
16
  /** Mirrors MAX_TEAM_EGRESS_DOMAINS in the shared read-side validator
17
17
  * (workers/session-coordinator/src/egress-domains.ts). The coordinator only
18
18
  * applies the first this-many UNIQUE valid domains at policy compose; the
19
19
  * CLI is a standalone package and cannot import the worker module, so the
20
20
  * cap constant is duplicated here (kept in sync via that file's tests). */
21
21
  const MAX_ADDITIVE_EGRESS_DOMAINS = 100;
22
- function fetcherFrom(io) {
23
- return io.fetcher ?? authedFetch;
24
- }
25
22
  function argValue(args, flag) {
26
23
  const index = args.indexOf(flag);
27
24
  if (index === -1 || index + 1 >= args.length || args[index + 1].startsWith('--')) {
@@ -45,23 +42,13 @@ function printErr(io, message) {
45
42
  ;
46
43
  (io.printErr ?? io.print)(message);
47
44
  }
48
- async function responseMessage(response) {
49
- try {
50
- const data = (await response.clone().json());
51
- const error = typeof data.error === 'string' ? data.error : null;
52
- return error || response.statusText || String(response.status);
53
- }
54
- catch {
55
- return response.statusText || String(response.status);
56
- }
57
- }
58
- function splitDomains(value) {
45
+ export function splitDomains(value) {
59
46
  return value
60
47
  .split(',')
61
48
  .map(d => d.trim())
62
49
  .filter(d => d.length > 0);
63
50
  }
64
- function stringArray(value) {
51
+ export function stringArray(value) {
65
52
  return Array.isArray(value) ? value.filter((d) => typeof d === 'string') : [];
66
53
  }
67
54
  /**
@@ -75,7 +62,7 @@ function stringArray(value) {
75
62
  * shrink-by-one on an oversized legacy row leaves the returned set still
76
63
  * over-cap and the server does not send cappedDomains back.
77
64
  */
78
- function deriveCapped(stored, invalid) {
65
+ export function deriveCapped(stored, invalid) {
79
66
  const invalidSet = new Set(invalid);
80
67
  const capped = [];
81
68
  let validCount = 0;
@@ -88,19 +75,7 @@ function deriveCapped(stored, invalid) {
88
75
  }
89
76
  return capped;
90
77
  }
91
- async function fetchCurrent(fetcher, path) {
92
- const response = await fetcher(path, { method: 'GET' });
93
- if (!response.ok) {
94
- throw new Error(`show failed (${response.status}): ${await responseMessage(response)}`);
95
- }
96
- const data = (await response.json());
97
- return {
98
- domains: stringArray(data.extraDomains),
99
- invalid: stringArray(data.invalidDomains),
100
- capped: stringArray(data.cappedDomains),
101
- };
102
- }
103
- function emit(io, teamSlug, current) {
78
+ export function emit(io, teamSlug, current) {
104
79
  if (io.json) {
105
80
  io.print(JSON.stringify({
106
81
  teamSlug,
@@ -134,61 +109,13 @@ export async function egressAllowlistCommand(args, io) {
134
109
  printErr(io, 'Usage: orizu team egress-allowlist <team> [--add <domain>|--remove <domain>|--set a.com,b.com|--clear] [--json]');
135
110
  return 1;
136
111
  }
137
- const path = `/api/cli/teams/${encodeURIComponent(teamSlug)}/egress-allowlist`;
138
- const fetcher = fetcherFrom(io);
139
- const setValue = argValue(args, '--set');
140
- const addValue = argValue(args, '--add');
141
- const removeValue = argValue(args, '--remove');
142
- const clear = args.includes('--clear');
143
- const isMutation = setValue !== null || addValue !== null || removeValue !== null || clear;
144
- try {
145
- if (!isMutation) {
146
- emit(io, teamSlug, await fetchCurrent(fetcher, path));
147
- return 0;
148
- }
149
- // Compute the next set. --set/--clear replace; --add/--remove mutate current.
150
- let next;
151
- if (setValue !== null) {
152
- next = splitDomains(setValue);
153
- }
154
- else if (clear) {
155
- next = [];
156
- }
157
- else {
158
- const current = await fetchCurrent(fetcher, path);
159
- const set = new Set(current.domains);
160
- if (addValue)
161
- for (const d of splitDomains(addValue))
162
- set.add(d);
163
- if (removeValue)
164
- for (const d of splitDomains(removeValue))
165
- set.delete(d);
166
- next = [...set];
167
- }
168
- const response = await fetcher(path, {
169
- method: 'PUT',
170
- headers: { 'Content-Type': 'application/json' },
171
- body: JSON.stringify({ domains: next }),
172
- });
173
- if (!response.ok) {
174
- printErr(io, `egress-allowlist update failed (${response.status}): ${await responseMessage(response)}`);
175
- return 1;
176
- }
177
- const data = (await response.json());
178
- const stored = Array.isArray(data.extraDomains) ? stringArray(data.extraDomains) : next;
179
- const invalid = stringArray(data.invalidDomains);
180
- // Grandfathered retention: a successful PUT may still STORE legacy
181
- // invalid entries the admin retained — the server flags them and we show
182
- // it. Round 5 (P2): a >100-entry legacy row can only shrink one entry per
183
- // PUT (the write RPC's GREATEST(100, stored-1) bound), so the returned
184
- // set can still be OVER the cap. Re-derive the cap annotations from the
185
- // returned set — same positional logic GET uses — instead of assuming
186
- // the RPC left it at/under 100.
187
- emit(io, teamSlug, { domains: stored, invalid, capped: deriveCapped(stored, invalid) });
188
- return 0;
189
- }
190
- catch (error) {
191
- printErr(io, error instanceof Error ? error.message : String(error));
192
- return 1;
193
- }
112
+ const { runEgressAllowlist } = await import('./egress-allowlist-workflow.js');
113
+ return await runEgressAllowlist({
114
+ teamSlug,
115
+ path: `/api/cli/teams/${encodeURIComponent(teamSlug)}/egress-allowlist`,
116
+ setValue: argValue(args, '--set'),
117
+ addValue: argValue(args, '--add'),
118
+ removeValue: argValue(args, '--remove'),
119
+ clear: args.includes('--clear'),
120
+ }, io);
194
121
  }
@@ -0,0 +1,17 @@
1
+ import { type EgressAllowlistCommandIo } from './egress-allowlist-cli.js';
2
+ /** What one run does: show the list, or change it. */
3
+ export interface EgressAllowlistRun {
4
+ teamSlug: string;
5
+ path: string;
6
+ setValue: string | null;
7
+ addValue: string | null;
8
+ removeValue: string | null;
9
+ clear: boolean;
10
+ }
11
+ /**
12
+ * Runs one `orizu team egress-allowlist` command to its exit code. Ctrl-C or
13
+ * SIGTERM ends the wait with 130 or 143; once the PUT was sent the person is
14
+ * told the update may be saved.
15
+ */
16
+ export declare function runEgressAllowlist(run: EgressAllowlistRun, io: EgressAllowlistCommandIo): Promise<number>;
17
+ //# sourceMappingURL=egress-allowlist-workflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"egress-allowlist-workflow.d.ts","sourceRoot":"","sources":["../src/egress-allowlist-workflow.ts"],"names":[],"mappings":"AAaA,OAAO,EAML,KAAK,wBAAwB,EAE9B,MAAM,2BAA2B,CAAA;AAoFlC,sDAAsD;AACtD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,KAAK,EAAE,OAAO,CAAA;CACf;AAkDD;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,kBAAkB,EAAE,EAAE,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CA6B/G"}
@@ -0,0 +1,183 @@
1
+ // ORI-2764 (ORI-2735c): `orizu team egress-allowlist` on Effect. Loaded only
2
+ // through `await import(...)` from egress-allowlist-cli.ts, so other commands
3
+ // never load Effect.
4
+ //
5
+ // - Each request has the dataset-transfer deadline (120 s by default).
6
+ // - The PUT is the only write. Once it was sent, a lost or late reply may
7
+ // still be saved. Every change is safe to repeat (`--set`/`--clear` replace
8
+ // the list, `--add`/`--remove` read it again first), so the CLI says
9
+ // running the same command again is safe.
10
+ // - Ctrl-C and SIGTERM stop a wait with 130 and 143.
11
+ import { Cause, Effect, Exit } from 'effect';
12
+ import { datasetTransferTimeoutMs } from './dataset-transfer-policy.js';
13
+ import { deriveCapped, emit, splitDomains, stringArray, } from './egress-allowlist-cli.js';
14
+ import { authedFetch } from './http.js';
15
+ import { mayAlreadyBeSavedLine } from './one-request-write.js';
16
+ import { unreachedCode } from './request-outcome.js';
17
+ const MAY_BE = mayAlreadyBeSavedLine('egress allowlist', 'updated', 'running the same command again is safe');
18
+ /** A failure the command prints as one message, plus the may-be line when the update may be saved. */
19
+ class EgressFailure {
20
+ message;
21
+ mayBeSaved;
22
+ constructor(message, mayBeSaved = false) {
23
+ this.message = message;
24
+ this.mayBeSaved = mayBeSaved;
25
+ }
26
+ }
27
+ /** The server's `{ error }`, else the status text, else the status, as before. */
28
+ function replyMessage(reply) {
29
+ try {
30
+ const data = JSON.parse(reply.text);
31
+ const error = typeof data.error === 'string' ? data.error : null;
32
+ return error || reply.statusText || String(reply.status);
33
+ }
34
+ catch {
35
+ return reply.statusText || String(reply.status);
36
+ }
37
+ }
38
+ /**
39
+ * One request. A deadline says so under `label`; any other failure keeps
40
+ * today's text. For the PUT, a failure after sending may be saved.
41
+ */
42
+ function request(fetcher, path, init, label, timeoutMs, progress) {
43
+ return Effect.suspend(() => {
44
+ let sent = false;
45
+ let timedOut = false;
46
+ return Effect.tryPromise({
47
+ try: async (signal) => {
48
+ const controller = new AbortController();
49
+ const abort = () => controller.abort();
50
+ const timer = setTimeout(() => { timedOut = true; controller.abort(); }, timeoutMs);
51
+ if (signal.aborted)
52
+ abort();
53
+ else
54
+ signal.addEventListener('abort', abort, { once: true });
55
+ const markSent = () => { sent = true; if (progress)
56
+ progress.putSent = true; };
57
+ try {
58
+ const requestInit = { ...init, signal: controller.signal };
59
+ const response = fetcher
60
+ ? (markSent(), await fetcher(path, requestInit))
61
+ : await authedFetch(path, requestInit, markSent);
62
+ return { status: response.status, ok: response.ok, statusText: response.statusText, text: await response.text() };
63
+ }
64
+ finally {
65
+ clearTimeout(timer);
66
+ signal.removeEventListener('abort', abort);
67
+ controller.abort();
68
+ }
69
+ },
70
+ catch: cause => {
71
+ const mayBeSaved = progress !== null && sent;
72
+ if (timedOut)
73
+ return new EgressFailure(`${label}: the request exceeded its deadline`, mayBeSaved);
74
+ // Refused before it reached the server, or a 401 whose sign-in could not be renewed: nothing saved.
75
+ const refused = unreachedCode(cause) !== null || cause?.requestRejected === true;
76
+ return new EgressFailure(cause instanceof Error ? cause.message : String(cause), mayBeSaved && !refused);
77
+ },
78
+ });
79
+ });
80
+ }
81
+ /** A 2xx reply's JSON object; a non-JSON reply fails with the parse error, as before. */
82
+ function parseReply(reply) {
83
+ return Effect.try({
84
+ try: () => JSON.parse(reply.text),
85
+ catch: cause => new EgressFailure(cause instanceof Error ? cause.message : String(cause)),
86
+ });
87
+ }
88
+ function readCurrent(fetcher, path, timeoutMs) {
89
+ return Effect.gen(function* () {
90
+ const reply = yield* request(fetcher, path, { method: 'GET' }, 'show failed', timeoutMs, null);
91
+ if (!reply.ok)
92
+ return yield* Effect.fail(new EgressFailure(`show failed (${reply.status}): ${replyMessage(reply)}`));
93
+ const data = yield* parseReply(reply);
94
+ return { domains: stringArray(data.extraDomains), invalid: stringArray(data.invalidDomains), capped: stringArray(data.cappedDomains) };
95
+ });
96
+ }
97
+ const isMutation = (run) => run.setValue !== null || run.addValue !== null || run.removeValue !== null || run.clear;
98
+ function egressAllowlist(run, io, timeoutMs, progress) {
99
+ return Effect.gen(function* () {
100
+ if (!isMutation(run)) {
101
+ emit(io, run.teamSlug, yield* readCurrent(io.fetcher, run.path, timeoutMs));
102
+ return 0;
103
+ }
104
+ // --set/--clear replace; --add/--remove change the list read first.
105
+ let next;
106
+ if (run.setValue !== null) {
107
+ next = splitDomains(run.setValue);
108
+ }
109
+ else if (run.clear) {
110
+ next = [];
111
+ }
112
+ else {
113
+ const current = yield* readCurrent(io.fetcher, run.path, timeoutMs);
114
+ const set = new Set(current.domains);
115
+ if (run.addValue)
116
+ for (const d of splitDomains(run.addValue))
117
+ set.add(d);
118
+ if (run.removeValue)
119
+ for (const d of splitDomains(run.removeValue))
120
+ set.delete(d);
121
+ next = [...set];
122
+ }
123
+ const label = 'egress-allowlist update failed';
124
+ const reply = yield* request(io.fetcher, run.path, {
125
+ method: 'PUT',
126
+ headers: { 'Content-Type': 'application/json' },
127
+ body: JSON.stringify({ domains: next }),
128
+ }, label, timeoutMs, progress);
129
+ if (!reply.ok) {
130
+ // A refusal (4xx) saved nothing; a 5xx after the PUT may have.
131
+ return yield* Effect.fail(new EgressFailure(`${label} (${reply.status}): ${replyMessage(reply)}`, reply.status >= 500));
132
+ }
133
+ const data = yield* parseReply(reply);
134
+ const stored = Array.isArray(data.extraDomains) ? stringArray(data.extraDomains) : next;
135
+ const invalid = stringArray(data.invalidDomains);
136
+ // Grandfathered retention: a successful PUT may still STORE legacy
137
+ // invalid entries the admin retained — the server flags them and we show
138
+ // it. Round 5 (P2): a >100-entry legacy row can only shrink one entry per
139
+ // PUT (the write RPC's GREATEST(100, stored-1) bound), so the returned
140
+ // set can still be OVER the cap. Re-derive the cap annotations from the
141
+ // returned set — same positional logic GET uses — instead of assuming
142
+ // the RPC left it at/under 100.
143
+ emit(io, run.teamSlug, { domains: stored, invalid, capped: deriveCapped(stored, invalid) });
144
+ return 0;
145
+ });
146
+ }
147
+ /**
148
+ * Runs one `orizu team egress-allowlist` command to its exit code. Ctrl-C or
149
+ * SIGTERM ends the wait with 130 or 143; once the PUT was sent the person is
150
+ * told the update may be saved.
151
+ */
152
+ export async function runEgressAllowlist(run, io) {
153
+ const printErr = io.printErr ?? io.print;
154
+ const controller = new AbortController();
155
+ const progress = { putSent: false };
156
+ let interrupted;
157
+ const interrupt = () => { interrupted ??= 130; controller.abort(); };
158
+ const terminate = () => { interrupted ??= 143; controller.abort(); };
159
+ process.on('SIGINT', interrupt);
160
+ process.on('SIGTERM', terminate);
161
+ try {
162
+ const exit = await Effect.runPromiseExit(egressAllowlist(run, io, datasetTransferTimeoutMs(), progress), { signal: controller.signal });
163
+ if (Exit.isSuccess(exit))
164
+ return exit.value;
165
+ if (interrupted !== undefined) {
166
+ const message = isMutation(run) ? 'Egress allowlist update interrupted.' : 'Egress allowlist read interrupted.';
167
+ printErr(progress.putSent ? `${message}\n${MAY_BE}` : message);
168
+ return interrupted;
169
+ }
170
+ // Anything else that went wrong prints its message with exit 1, as before.
171
+ const failure = Cause.squash(exit.cause);
172
+ if (!(failure instanceof EgressFailure)) {
173
+ printErr(failure instanceof Error ? failure.message : String(failure));
174
+ return 1;
175
+ }
176
+ printErr(failure.mayBeSaved ? `${failure.message}\n${MAY_BE}` : failure.message);
177
+ return 1;
178
+ }
179
+ finally {
180
+ process.removeListener('SIGINT', interrupt);
181
+ process.removeListener('SIGTERM', terminate);
182
+ }
183
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAuLA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAEhE,eAAO,MAAM,wBAAwB,GAAI,iBAAiB,MAAM;;;EACJ,CAAA;AAE5D,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAmqCtD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CA0B1G;AAED,eAAO,MAAM,iBAAiB,SAAU,CAAA;AACxC,eAAO,MAAM,uBAAuB,QAAkB,CAAA;AACtD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAexH;AAmMD,wBAAsB,IAAI,CAAC,OAAO,WAAwB,iBAiiBzD;AAuED,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAEhF,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAuLA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAEhE,eAAO,MAAM,wBAAwB,GAAI,iBAAiB,MAAM;;;EACJ,CAAA;AAE5D,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAmqCtD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CA0B1G;AAED,eAAO,MAAM,iBAAiB,SAAU,CAAA;AACxC,eAAO,MAAM,uBAAuB,QAAkB,CAAA;AACtD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAexH;AAmMD,wBAAsB,IAAI,CAAC,OAAO,WAAwB,iBAuiBzD;AAuED,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAEhF,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA"}
package/dist/index.js CHANGED
@@ -1565,7 +1565,13 @@ export async function main(rawArgs = process.argv.slice(2)) {
1565
1565
  return;
1566
1566
  }
1567
1567
  if (command === 'apps' && subcommand === 'preview') {
1568
- await previewAppFromFile();
1568
+ const stopped = await previewAppFromFile();
1569
+ if (stopped !== undefined) {
1570
+ process.exitCode = stopped;
1571
+ // A Chromium launch can't be cancelled; one still starting holds the process
1572
+ // until it arrives and is closed. Exit anyway once the 10 s budget is spent.
1573
+ setTimeout(() => process.exit(stopped), 10_000).unref();
1574
+ }
1569
1575
  return;
1570
1576
  }
1571
1577
  if (command === 'apps' && subcommand === 'update') {