@isomorph.ai/cli 0.10.5 → 0.10.7

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.
package/README.md CHANGED
@@ -56,3 +56,17 @@ There are two ways onto Isomorph and `init` is where the folder decides. An empt
56
56
  Built from the [governance control plane](https://github.com/Fourier-Labs-AI/harbour-governance-control-plane) repository: `npm ci`, `npm run build --prefix packages/harbour-cli`, tests in `tests/cli-kit.test.ts` and `tests/cli-packaging-contract.test.ts`. Merge is release (`docs/adr/0021`): a PR that bumps the version in `packages/harbour-cli/package.json` — the one file that carries it; `src/version.ts` reads it at runtime — is tagged `cli-v<version>` by `CLI release automation` the moment it lands on `main`, and `Release Isomorph CLI` publishes that version under `latest` through npm trusted publishing. A version that already exists on npm is never republished. A prerelease (`X.Y.Z-rc.N`) publishes under `candidate` and is never required. When the release embeds a new kit bundle, bump `harbour.kitBundle.version` in the same PR and run `npm run kit-bundle:sync` there: it regenerates the embedded manifest and the server's `src/kit-bundle-expectation.generated.ts` from one fetch. 0.2.0 and 0.2.1 (the rename) were published by hand from their merge commits before the trusted publisher existed for `@isomorph.ai/cli`, which is why their `cli-v` release runs show red.
57
57
 
58
58
  Connect with `isomorph connect name@getlokal.com`: the CLI asks the platform which company admits the email's domain (`https://platform.isomorph.ai/start/getlokal.com.json` — the same policies that admit the person at sign-in) and saves that company's profile. Only the domain is sent, not the email. When no company admits the domain, or more than one does, the CLI prints the platform's sentence (ask IT, or run the printed `isomorph connect <link>`) and saves nothing; a company's console page (`platform.isomorph.ai/t/<company>`) is accepted as the link. `isomorph login` then prints the company it signed in to and what IT has set up there. Company sign-in and access checks still apply.
59
+
60
+ ### Owner-authorised background integrations
61
+
62
+ Read `isomorph integrations status --app-root . --json` for `consents`: the caller's connected
63
+ accounts and per-environment background flags. Include background use of the account by the app
64
+ in the setup approval, naming the account and environment; reuse explicit approval already given.
65
+ Then run `isomorph integrations request <connection> --app-root . --allow-background preview --json`
66
+ and verify status. Omit the flag for ordinary requests. This records consent on the existing link;
67
+ it neither approves IT access nor sends a message. The existing console control can revoke it.
68
+
69
+ If the account needs connecting, use `integrations connect <connection> --app-root .
70
+ --return-url <dev-origin>/_harbour/integrations/oauth/complete --json` and follow `nextStep`.
71
+ Run `isomorph agent-setup` after updating the CLI to refresh the AI instructions; `init` also
72
+ installs them. No new endpoint, login, runtime or SDK change is needed.
@@ -123,7 +123,7 @@ Rules live beside this file: \`core.md\` before the first edit; \`integrations.m
123
123
  | "ship it", "let my team try it" | show the person the three values in \`.isomorph/app.json\` (name, description, audience — "only you" when empty) and correct the file as they say; then \`isomorph deploy --app-root . --json\` (it runs the checks when needed and refuses once naming every blocker and its fix: relay that line; the app works meanwhile). Give them \`result.deployment.protectedUrl\`. \`--session-notes\`: what cost you time in Isomorph, or \`""\`. |
124
124
  | "make it live" | only after they have tried the preview: \`isomorph promote --operation <ref> --app-root . --confirm-tested --json\`. Report the production link, or that operator approval is pending. |
125
125
  | "stop it" | \`isomorph stop --app-root .\` (data kept); \`isomorph dev --app-root . --reset\` only when they ask to start over. |
126
- | "every day at 2pm", "send this automatically" | read \`jobs.md\` beside this skill first (the owner must enable background use of their account); write the job, run it once with \`isomorph jobs run\`, and say its sentence. |
126
+ | "every day at 2pm", "send this automatically" | read \`jobs.md\` beside this skill first (follow its in-conversation owner approval steps); write the job, run it once with \`isomorph jobs run\`, and report what was verified. |
127
127
  | "call this API", "a key for it", "run it in the background" | read \`server.md\` beside this skill first; server code goes in \`actions/\` or \`jobs/\`, a key by its name only. |
128
128
 
129
129
  A refusal names its layer, its reason and its fix: change that one thing, then run it again. A wait timeout is not a failure — work is still running: run the command it prints (\`${continueCommand("<ref>")}\`); never start a second deploy while one is running.
@@ -13,7 +13,7 @@ import { agentPaths, agentSetup } from "./agent-setup.js";
13
13
  import { detachDev, startDev } from "./dev.js";
14
14
  import { endProcess, installDependencies, LocalRuntime, readDevLock, releaseDevLock, runCommand } from "./local-runtime.js";
15
15
  import { CHECKS_FAILED_HINT, checksFailedMessage, runChecks } from "./check.js";
16
- import { assertDeployReady, companySystemsLine, GovernanceClient, groupGrants, IDENTITY_WORDS, integrationsCatalog, integrationsStatus, renderGrantGroup, renderIntegrationsCatalog, requestIntegrations } from "./integrations.js";
16
+ import { connectIntegration, assertDeployReady, companySystemsLine, GovernanceClient, groupGrants, IDENTITY_WORDS, integrationsCatalog, integrationsStatus, renderGrantGroup, renderIntegrationsCatalog, requestIntegrations } from "./integrations.js";
17
17
  import { runJob } from "./jobs.js";
18
18
  const args = process.argv.slice(2);
19
19
  // `productionise` (the command's name until 0.3.4) was an alias of `deploy` for one release; it is no longer a command and falls through to the usage error.
@@ -39,6 +39,7 @@ const out = optionValue("--out");
39
39
  const testIntegrations = args.includes("--integrations");
40
40
  const reason = optionValue("--reason");
41
41
  const operations = optionValue("--operations");
42
+ const allowBackground = optionValue("--allow-background");
42
43
  const scheduledAt = optionValue("--scheduled-at");
43
44
  const realJob = args.includes("--real");
44
45
  /** Seconds `deploy`, `status --wait`, `retry` and `promote` follow the operation before reporting it as still running (default: 30 minutes). */
@@ -47,6 +48,8 @@ const waitOptions = maxWaitSeconds ? { maxWaitMs: maxWaitSeconds * 1000 } : {};
47
48
  let optionError;
48
49
  if (maxWaitSeconds !== undefined && !(Number.isInteger(maxWaitSeconds) && maxWaitSeconds > 0))
49
50
  optionError = "--max-wait takes a whole number of seconds.";
51
+ if (args.includes("--allow-background") && !["development", "preview", "production"].includes(allowBackground ?? ""))
52
+ optionError = "--allow-background takes development, preview or production.";
50
53
  const explicitToken = process.env.ISOMORPH_TOKEN?.trim() ?? "";
51
54
  const usage = [
52
55
  "Usage:",
@@ -63,7 +66,8 @@ const usage = [
63
66
  " isomorph retry [--app-root <path>] [--operation <reference>] [--no-wait] [--max-wait <seconds>] [--json]",
64
67
  " isomorph promote [--app-root <path>] [--operation <reference>] [--confirm-tested] [--no-wait] [--max-wait <seconds>] [--json] (--confirm-tested: the person has tried the preview; the company's AI setup is confirmed for production when the app calls governed AI)",
65
68
  " isomorph jobs run <name> --app-root <path> [--scheduled-at <UTC>] [--real] [--json] run one scheduled job now (local fixtures; --real uses approved company access)",
66
- " isomorph integrations request <connection> --app-root <path> [--reason <text>] [--operations a,b] [--json] one request per connection, for every environment at once: IT approves it once (isomorph dev and deploy file it for you)",
69
+ " isomorph integrations request <connection> --app-root <path> [--reason <text>] [--operations a,b] [--allow-background <development|preview|production>] [--json] one request per connection, for every environment at once: IT approves it once (isomorph dev and deploy file it for you)",
70
+ " isomorph integrations connect <connection> --app-root <path> [--return-url <local-app-url>] [--json] connect the signed-in account; returns provider authorizationUrl when needed",
67
71
  " isomorph integrations status --app-root <path> [--json]",
68
72
  " isomorph integrations catalog --app-root <path> [--json] the company's connections as .isomorph/integrations.json names them: identifiers, allowed operations, approved channels/tables/views/mailboxes per environment (no app needed)",
69
73
  "Run `isomorph connect <work-email>` once; sign in with `isomorph login` when Isomorph asks.",
@@ -110,11 +114,11 @@ else if (command === "agent-setup") {
110
114
  }
111
115
  else if (!["connect", "login", "logout", "deploy", "integrations", ...LOCAL_COMMANDS, ...OPERATION_COMMANDS].includes(command)
112
116
  || (command === "connect" && (!connectUrl || connectUrl.startsWith("--")))
113
- || (["deploy", ...OPERATION_COMMANDS].includes(command) && optionError)
117
+ || (["deploy", "integrations", ...OPERATION_COMMANDS].includes(command) && optionError)
114
118
  || (command === "deploy" && !root)
115
119
  || (LOCAL_COMMANDS.includes(command) && !root)
116
120
  || (command === "jobs" && (subcommand !== "run" || !args[2] || args[2].startsWith("--")))
117
- || (command === "integrations" && (!root || !subcommand || !["request", "status", "catalog"].includes(subcommand) || (subcommand === "request" && (!args[2] || args[2].startsWith("--")))))) {
121
+ || (command === "integrations" && (!root || !subcommand || !["request", "status", "catalog", "connect"].includes(subcommand) || (["request", "connect"].includes(subcommand) && (!args[2] || args[2].startsWith("--")))))) {
118
122
  exitAfterWriting(2, "", `${optionError ? `${optionError}\n` : ""}${usage}`);
119
123
  }
120
124
  else {
@@ -246,14 +250,20 @@ else {
246
250
  let envelope;
247
251
  if (command === "integrations") {
248
252
  const target = appRoot(root);
249
- const result = subcommand === "status"
250
- ? await integrationsStatus(target, governance)
251
- : subcommand === "catalog"
252
- ? await integrationsCatalog(governance)
253
- : await requestIntegrations(target, governance, tenant, EMBEDDED_KIT_BUNDLE, { connection: args[2], ...(reason ? { reason } : {}), operations: operations?.split(",").map(value => value.trim()).filter(Boolean) });
253
+ const result = subcommand === "connect"
254
+ ? await connectIntegration(target, governance, args[2], optionValue("--return-url"))
255
+ : subcommand === "status"
256
+ ? await integrationsStatus(target, governance)
257
+ : subcommand === "catalog"
258
+ ? await integrationsCatalog(governance)
259
+ : await requestIntegrations(target, governance, tenant, EMBEDDED_KIT_BUNDLE, { connection: args[2], ...(reason ? { reason } : {}), operations: operations?.split(",").map(value => value.trim()).filter(Boolean), allowBackground });
254
260
  envelope = summaryEnvelope(result);
255
- if (!json)
256
- progress(subcommand === "status" ? renderIntegrationsStatus(result) : subcommand === "catalog" ? renderIntegrationsCatalog(result) : renderRequest(result));
261
+ if (!json) {
262
+ if ("nextStep" in result)
263
+ progress([result.authorizationUrl, result.nextStep].filter(Boolean).join("\n"));
264
+ else
265
+ progress(subcommand === "status" ? renderIntegrationsStatus(result) : subcommand === "catalog" ? renderIntegrationsCatalog(result) : renderRequest(result));
266
+ }
257
267
  }
258
268
  else if (command === "deploy") {
259
269
  const result = await deploy(root, governance, progress, tenant, { waitForDeployment: !noWait, waitOptions, bundle: EMBEDDED_KIT_BUNDLE, ...(sessionNotes ? { sessionNotes } : {}) });
@@ -299,6 +309,8 @@ function renderIntegrationsStatus(status) {
299
309
  if (!status.linked)
300
310
  return "This app is not linked yet; `isomorph dev` (signed in) or `isomorph integrations request` links it.";
301
311
  const lines = [`App ${status.appId}`, ...groupGrants(status.grants).flatMap(group => renderGrantGroup(group).map(line => ` ${line}`))];
312
+ for (const consent of status.consents ?? [])
313
+ lines.push(` ${consent.connection} (${consent.environment}): ${consent.accountLabel ?? "your account"}, ${consent.status}, background ${consent.backgroundAllowed ? "allowed" : "off"}`);
302
314
  for (const request of status.requests)
303
315
  lines.push(` request ${request.requestId}: ${request.state}${request.reason ? ` (${request.reason})` : ""}`);
304
316
  if (!status.grants.length && !status.requests.length)
@@ -306,7 +318,7 @@ function renderIntegrationsStatus(status) {
306
318
  return lines.join("\n");
307
319
  }
308
320
  function renderRequest(result) {
309
- return [`App ${result.appId}: ${result.connection}`, ...result.requests.map(item => ` ${IDENTITY_WORDS[item.identityMode]} — ${item.operations.join(", ")} on ${item.resources.map(requestResourceName).join(", ")}${item.presentation ? ` (posts as "${item.presentation.displayName}")` : ""}: ${item.state} — ${item.summary}`)].join("\n");
321
+ return [`App ${result.appId}: ${result.connection}`, ...result.requests.map(item => ` ${IDENTITY_WORDS[item.identityMode]} — ${item.operations.join(", ")} on ${item.resources.map(requestResourceName).join(", ")}${item.presentation ? ` (posts as "${item.presentation.displayName}")` : ""}: ${item.state} — ${item.summary}${item.backgroundConsent ? `; background allowed in ${item.backgroundConsent.environment}` : ""}`)].join("\n");
310
322
  }
311
323
  /**
312
324
  * Who signed in and what IT has set up, for the line after `login`; undefined
@@ -91,13 +91,16 @@ const reply = await isomorph.ai.chat({ messages: [{ role: "user", content: \`Sum
91
91
  The starter calls no AI; add the one call when the person asks. The check's generated ai journey makes a real governed call locally through the development route with your \`isomorph login\`; in the pipeline it is answered by a canned completion and reported as not tested.`,
92
92
  jobs: `# Scheduled jobs
93
93
 
94
- A job runs as the app. It may use Gmail or user-mode Slack after the app owner explicitly enables background use of their connected account for that app and environment. Without that permission, user-mode calls are refused.
94
+ Jobs run as the app. Gmail and user-mode Slack require the current owner's connected account, an approved integration grant and explicit background permission in each environment. App-mode Slack needs no personal background consent.
95
95
 
96
- Scheduled work lives only in \`jobs/<name>.ts\`: export one literal UTC cron as \`schedule\` and one default async handler. Never use \`setInterval\`, an effect or a browser timer as a scheduler; Isomorph runs the same declaration automatically after deployment on the app's server process, which owns the real clock. A job may call \`isomorph.ai.chat\` and approved company operations. A job with no \`schedule\` runs only when enqueued (server.md). Test it immediately: start \`isomorph dev\`, then \`isomorph jobs run <name> --app-root . --scheduled-at <matching-UTC-time> --json\` — local data, canned AI, company systems refused, nothing sent. Use \`--real\` only when the person explicitly asks to test the company action now and the exact action has development approval.
96
+ Write \`jobs/<name>.ts\` with one literal UTC cron as \`schedule\` and a default async handler. Never use \`setInterval\`, an effect or a browser timer: the deployed app server owns the clock. Jobs can call approved company operations and \`isomorph.ai.chat\`; without a schedule they run only when enqueued (server.md). Use a deterministic idempotency key per business period and destination.
97
97
 
98
- A real scheduled send requires the app owner to enable background use of their account, the operation and resource declared in \`.isomorph/integrations.json\`, and a grant for that environment; use one deterministic idempotency key for the business period and destination so a replay does not silently repost.
98
+ For personal background access:
99
+ 1. Run \`isomorph integrations status --app-root . --json\`; inspect \`consents\` for the connected account and per-environment \`backgroundAllowed\`.
100
+ 2. If connection is missing or needs reconnecting, run \`isomorph dev\`, then \`isomorph integrations connect <connection> --app-root . --return-url <origin>/_harbour/integrations/oauth/complete --json\`. Follow \`nextStep\`; the person completes provider consent if required.
101
+ 3. Include use of this account by this app's background code in the setup approval, naming the account and environment. Reuse explicit approval already given; no separate approval step. Then run \`isomorph integrations request <connection> --app-root . --allow-background <development|preview|production> --json\` and verify status. Do not send the person to the Admin console. Omit the flag when background use was not authorised.
99
102
 
100
- Tell them: "Your scheduled task is ready. I can test the app on your computer now. Company messages will start after the app is online and access is approved."`,
103
+ Test with \`isomorph jobs run <name> --app-root . --scheduled-at <matching-UTC-time> --json\`: local data, canned AI, company systems refused, nothing sent. Add \`--real\` only for an explicitly requested immediate test with development approval and background consent. Permission alone does not deploy the job; verify deployment and actual delivery before calling the schedule active.`,
101
104
  server: `# Server code: actions, queued runs, outside APIs
102
105
 
103
106
  Isomorph runs one server process for the app, started by \`isomorph dev\`, the check and the deployment. It serves \`actions/<name>.ts\` and \`jobs/<name>.ts\`; both use the same \`isomorph\` client and nothing else — no database driver, no \`process.env.DATABASE_URL\`, no HTTP framework.
@@ -29,6 +29,9 @@ export class GovernanceClient {
29
29
  catalog() {
30
30
  return this.call("GET", `/v1/development/integrations/catalog?tenantId=${encodeURIComponent(this.tenantId)}`);
31
31
  }
32
+ connectIntegration(appId, connection, returnUrl) {
33
+ return this.call("POST", `/v1/development/apps/${encodeURIComponent(appId)}/integrations/connect`, { connection, ...(returnUrl ? { returnUrl } : {}) });
34
+ }
32
35
  execute(appId, body) {
33
36
  return this.call("POST", `/v1/development/apps/${encodeURIComponent(appId)}/integrations/execute`, body);
34
37
  }
@@ -141,11 +144,14 @@ export const IDENTITY_WORDS = { app: "as the company bot", user: "as the signed-
141
144
  /** A refusal about the leg rather than the ask — signed out, unreachable, an answer with no code — is nobody's ask and stops the run. */
142
145
  const legFailure = (code) => code === "AUTH_REQUIRED" || code === "GOVERNANCE_UNREACHABLE" || code.startsWith("HTTP_");
143
146
  /** One request per identity mode of the connection. A refusal of an ask is returned beside the others, not thrown, so a caller can report every connection at once. */
144
- async function submitRequests(client, appId, declaration, root, connection, app, reason, only) {
147
+ async function submitRequests(client, appId, declaration, root, connection, app, reason, only, allowBackground) {
145
148
  return Promise.all(requestScope(declaration, connection, only).map(async (part) => {
146
149
  const ask = { connection, ...part };
147
150
  try {
148
- return { ...ask, outcome: await client.request(appId, { connection, identityMode: part.identityMode, operations: part.operations, resources: part.resources, ...(part.presentation ? { presentation: part.presentation } : {}), ...(reason?.trim() ? { reason: reason.trim() } : {}), app }) };
151
+ const outcome = await client.request(appId, { connection, identityMode: part.identityMode, operations: part.operations, resources: part.resources, ...(part.presentation ? { presentation: part.presentation } : {}), ...(reason?.trim() ? { reason: reason.trim() } : {}), app, ...(allowBackground && part.identityMode === "user" ? { allowBackground } : {}) });
152
+ if (allowBackground && part.identityMode === "user" && (outcome.backgroundConsent?.environment !== allowBackground || outcome.backgroundConsent.backgroundAllowed !== true))
153
+ throw new CliError("BACKGROUND_NOT_CONFIRMED", "The server did not confirm background permission. Check integrations status before reporting it enabled.");
154
+ return { ...ask, outcome };
149
155
  }
150
156
  catch (error) {
151
157
  const refusal = unregisteredResourceGuidance(error, declaration, connection, root);
@@ -192,9 +198,11 @@ export async function fileDeclaredRequests(root, client, tenantId, bundle) {
192
198
  */
193
199
  export async function requestIntegrations(root, client, tenantId, bundle, options) {
194
200
  const declaration = await readDeclaration(root);
201
+ if (options.allowBackground !== undefined && (!ENVIRONMENTS.includes(options.allowBackground) || !requestScope(declaration, options.connection, options.operations).some(part => part.identityMode === "user")))
202
+ throw new CliError("INPUT_INVALID", "--allow-background requires user-mode access and one environment: development, preview or production.");
195
203
  const app = reviewedProfileBody(await readReviewedAppProfile(root));
196
204
  const appId = await ensureLinkedApp(root, client, tenantId, bundle);
197
- const submitted = settled(await submitRequests(client, appId, declaration, root, options.connection, app, options.reason, options.operations)).map(item => ({ ...item, state: item.outcome.state }));
205
+ const submitted = settled(await submitRequests(client, appId, declaration, root, options.connection, app, options.reason, options.operations, options.allowBackground)).map(item => ({ ...item, state: item.outcome.state }));
198
206
  const sleep = options.sleep ?? ((ms) => new Promise(resolve => setTimeout(resolve, ms)));
199
207
  const pollMs = options.pollMs ?? 3_000;
200
208
  let grants = [];
@@ -216,6 +224,7 @@ export async function requestIntegrations(root, client, tenantId, bundle, option
216
224
  }
217
225
  const groups = groupGrants(grants);
218
226
  return { appId, connection: options.connection, requests: submitted.map(item => ({
227
+ ...(item.outcome.backgroundConsent ? { backgroundConsent: item.outcome.backgroundConsent } : {}),
219
228
  identityMode: item.identityMode, operations: item.operations, resources: item.resources, ...(item.presentation ? { presentation: item.presentation } : {}),
220
229
  ...(item.outcome.requestId ? { requestId: item.outcome.requestId } : {}), grantId: item.outcome.grantId, state: item.state,
221
230
  readiness: item.state === "PENDING" ? "pending" : grants.find(grant => grant.grantId === item.outcome.grantId)?.readiness ?? (item.state === "READY" ? "ready" : "denied"),
@@ -463,5 +472,24 @@ export async function integrationsStatus(root, client) {
463
472
  if (!lock?.appId)
464
473
  return { linked: false, grants: [], requests: [] };
465
474
  const listed = await client.list(lock.appId);
466
- return { appId: lock.appId, linked: true, grants: listed.grants.map(grant => ({ connection: grant.connection, environment: grant.environment, identityMode: grant.identityMode, status: grant.status, operations: grant.operations, resources: grant.resources, expiresAt: grant.expiresAt, readiness: grant.readiness, provisioning: grant.provisioning, pendingRequestId: grant.pendingRequestId, failure: grant.failure })), requests: listed.requests };
475
+ return { appId: lock.appId, linked: true, grants: listed.grants.map(grant => ({ connection: grant.connection, environment: grant.environment, identityMode: grant.identityMode, status: grant.status, operations: grant.operations, resources: grant.resources, expiresAt: grant.expiresAt, readiness: grant.readiness, provisioning: grant.provisioning, pendingRequestId: grant.pendingRequestId, failure: grant.failure })), requests: listed.requests, consents: listed.consents ?? [] };
476
+ }
477
+ async function linkedApp(root) {
478
+ const lock = await readKitLock(root);
479
+ if (!lock?.appId)
480
+ throw new CliError("APP_NOT_LINKED", "Run isomorph dev or integrations request for this app first.");
481
+ return lock.appId;
482
+ }
483
+ export async function connectIntegration(root, client, connection, returnUrl) {
484
+ const appId = await linkedApp(root);
485
+ let result;
486
+ try {
487
+ result = await client.connectIntegration(appId, connection, returnUrl);
488
+ }
489
+ catch (error) {
490
+ if (!returnUrl && error instanceof CliError && error.code === "VALIDATION_FAILED")
491
+ throw new CliError(error.code, error.message, undefined, "Start isomorph dev, then repeat with --return-url <origin>/_harbour/integrations/oauth/complete using the returned local origin.");
492
+ throw error;
493
+ }
494
+ return { appId, connection, ...result, nextStep: result.status === "connected" ? "Account connected. Run integrations status, then integrations request with --allow-background for the approved environment; connecting never enables it." : "Open authorizationUrl in the browser and let the owner finish provider consent, then run this command again. Use --return-url with the running app's local URL if required." };
467
495
  }
@@ -1,28 +1,28 @@
1
1
  export const PUBLISHED_KIT_BUNDLE = {
2
2
  "schema": "isomorph.kit-bundle/1.0",
3
- "kitVersion": "0.10.2",
3
+ "kitVersion": "0.10.3",
4
4
  "sdk": {
5
5
  "package": "@isomorph.ai/app-sdk",
6
- "version": "1.3.1",
7
- "tarballSha256": "9daf8a67dac57ac920db3645aee64065e6a39c7397557fe393a3b02f99e5ff84",
8
- "url": "https://public.ecr.aws/v2/y6t4p3i8/harbour-kit-bundle/blobs/sha256:9daf8a67dac57ac920db3645aee64065e6a39c7397557fe393a3b02f99e5ff84"
6
+ "version": "1.4.1",
7
+ "tarballSha256": "440597e5d9579edb0c4206b60f1cd514efd18e45673d968d19e43a15e23c880e",
8
+ "url": "https://public.ecr.aws/v2/y6t4p3i8/harbour-kit-bundle/blobs/sha256:440597e5d9579edb0c4206b60f1cd514efd18e45673d968d19e43a15e23c880e"
9
9
  },
10
10
  "images": {
11
- "appGateway": "public.ecr.aws/y6t4p3i8/harbour-app-gateway@sha256:a51e529efafed298ee5d2b798a4895a85582f051633e3a060e82b53bcd6c85a8",
12
- "sessionFixture": "public.ecr.aws/y6t4p3i8/harbour-session-fixture@sha256:16f7178caaad9aa0a87db3dc07e09bb9ce57abd736f895260a1acbb4febbc058"
11
+ "appGateway": "public.ecr.aws/y6t4p3i8/harbour-app-gateway@sha256:18ad0a63ad44dedf34a22a5b95c268ed3e7a66a29e0c907a627e987ce4fcfcbd",
12
+ "sessionFixture": "public.ecr.aws/y6t4p3i8/harbour-session-fixture@sha256:d65c63820c622b6146d016fa6a05b33f4bbac742e59a1847c46bbbad6aa0c800"
13
13
  },
14
14
  "nativeRuntime": {
15
15
  "darwinArm64": {
16
- "url": "https://public.ecr.aws/v2/y6t4p3i8/harbour-kit-bundle/blobs/sha256:7c4c62abb5d69181bbac7bdd2d3245b8cf8a3c7eeb52a2e5cc949f8d1085c624",
17
- "sha256": "7c4c62abb5d69181bbac7bdd2d3245b8cf8a3c7eeb52a2e5cc949f8d1085c624"
16
+ "url": "https://public.ecr.aws/v2/y6t4p3i8/harbour-kit-bundle/blobs/sha256:ec28801920ed071b20903a738943889f6354736a8340ea4ae7c12eb4ab68530d",
17
+ "sha256": "ec28801920ed071b20903a738943889f6354736a8340ea4ae7c12eb4ab68530d"
18
18
  },
19
19
  "linuxX64": {
20
- "url": "https://public.ecr.aws/v2/y6t4p3i8/harbour-kit-bundle/blobs/sha256:08db811e262188518a52982a4d81dfa1db96e8aa10808ad1707b2c9583bc4a22",
21
- "sha256": "08db811e262188518a52982a4d81dfa1db96e8aa10808ad1707b2c9583bc4a22"
20
+ "url": "https://public.ecr.aws/v2/y6t4p3i8/harbour-kit-bundle/blobs/sha256:f0796313ae5aea070726947093ab622c953c3f820ab46af7727b19697a74c714",
21
+ "sha256": "f0796313ae5aea070726947093ab622c953c3f820ab46af7727b19697a74c714"
22
22
  },
23
23
  "windowsX64": {
24
- "url": "https://public.ecr.aws/v2/y6t4p3i8/harbour-kit-bundle/blobs/sha256:6fcf0f82da9607dc34d42422bcd3fd77aac15f5f9c1566177cf9dad4847dc1dd",
25
- "sha256": "6fcf0f82da9607dc34d42422bcd3fd77aac15f5f9c1566177cf9dad4847dc1dd"
24
+ "url": "https://public.ecr.aws/v2/y6t4p3i8/harbour-kit-bundle/blobs/sha256:bb507cdcf2acaf06b33622fa3df15895f0d548d4ce269ff9dbeb4c38d21b0e89",
25
+ "sha256": "bb507cdcf2acaf06b33622fa3df15895f0d548d4ce269ff9dbeb4c38d21b0e89"
26
26
  }
27
27
  },
28
28
  "brief": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isomorph.ai/cli",
3
- "version": "0.10.5",
3
+ "version": "0.10.7",
4
4
  "description": "Isomorph development kit CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -39,7 +39,7 @@
39
39
  "harbour": {
40
40
  "kitBundle": {
41
41
  "repository": "public.ecr.aws/y6t4p3i8/harbour-kit-bundle",
42
- "version": "0.10.2"
42
+ "version": "0.10.3"
43
43
  }
44
44
  }
45
45
  }