@notis_ai/cli 0.2.16 → 0.2.18

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.
@@ -7303,7 +7303,8 @@ async function pullAppSourceUnlocked({
7303
7303
  targetDir,
7304
7304
  version = "latest",
7305
7305
  force = false,
7306
- profileKey = null
7306
+ profileKey = null,
7307
+ expectedUpdatedAt = null
7307
7308
  }, assertLockOwnership, assertParentIdentity, initialTargetIdentity) {
7308
7309
  assertLockOwnership();
7309
7310
  assertParentIdentity();
@@ -7443,6 +7444,7 @@ async function pullAppSourceUnlocked({
7443
7444
  writeLinkedState(targetRoot, {
7444
7445
  app_id: appId,
7445
7446
  ...Number.isFinite(linkedVersion) ? { version: linkedVersion } : {},
7447
+ expected_updated_at: expectedUpdatedAt,
7446
7448
  linked_at: (/* @__PURE__ */ new Date()).toISOString()
7447
7449
  }, profileKey);
7448
7450
  assertPullTargetIdentity(targetDir, activeTargetIdentity);
@@ -14281,6 +14283,8 @@ async function appsCreateHandler(ctx) {
14281
14283
  if (projectDir) {
14282
14284
  writeLinkedState(projectDir, {
14283
14285
  app_id: app.id,
14286
+ version: deployedAppVersion(app),
14287
+ expected_updated_at: app.updated_at ?? null,
14284
14288
  linked_at: (/* @__PURE__ */ new Date()).toISOString()
14285
14289
  }, linkedStateProfileKey(ctx.runtime));
14286
14290
  }
@@ -15181,19 +15185,24 @@ function buildLinkedAppState(existingState, appId, linkedAt = (/* @__PURE__ */ n
15181
15185
  async function appsLinkHandler(ctx) {
15182
15186
  const projectDir = resolveProjectDir(ctx.args.dir || ".");
15183
15187
  const appId = ctx.args.appId;
15184
- await assertLinkTarget(ctx.runtime, appId);
15185
- writeLinkedState(
15186
- projectDir,
15187
- buildLinkedAppState(
15188
- readLinkedState(projectDir, linkedStateProfileKey(ctx.runtime)),
15189
- appId,
15190
- (/* @__PURE__ */ new Date()).toISOString()
15191
- ),
15192
- linkedStateProfileKey(ctx.runtime)
15193
- );
15188
+ const app = await assertLinkTarget(ctx.runtime, appId);
15189
+ const profileKey = linkedStateProfileKey(ctx.runtime);
15190
+ const previous = readLinkedState(projectDir, profileKey);
15191
+ const version = deployedAppVersion(app);
15192
+ if (previous?.app_id === appId && previous.version !== void 0 && previous.version !== version) {
15193
+ throw usageError("The app release changed. Preserve local source, pull the current release into a fresh directory, and reapply changes before deploying.");
15194
+ }
15195
+ if (typeof app.updated_at !== "string" || !app.updated_at.trim()) {
15196
+ throw usageError("App revision is unavailable; the directory was not relinked.");
15197
+ }
15198
+ writeLinkedState(projectDir, {
15199
+ ...buildLinkedAppState(previous, appId),
15200
+ version,
15201
+ expected_updated_at: app.updated_at
15202
+ }, profileKey);
15194
15203
  return ctx.output.emitSuccess({
15195
15204
  command: ctx.spec.command_path.join(" "),
15196
- data: { app_id: appId, project_dir: projectDir },
15205
+ data: { app_id: appId, project_dir: projectDir, version, expected_updated_at: app.updated_at },
15197
15206
  humanSummary: `Linked to app ${appId}`,
15198
15207
  hints: [
15199
15208
  { command: "notis apps deploy .", reason: "Deploy the app" }
@@ -15227,7 +15236,8 @@ async function appsPullHandler(ctx) {
15227
15236
  targetDir,
15228
15237
  version,
15229
15238
  force: Boolean(ctx.options.force),
15230
- profileKey: linkedStateProfileKey(ctx.runtime)
15239
+ profileKey: linkedStateProfileKey(ctx.runtime),
15240
+ expectedUpdatedAt: app.updated_at
15231
15241
  });
15232
15242
  const versionLabel = pulled.version === "latest" ? "latest version" : `v${pulled.version}`;
15233
15243
  return ctx.output.emitSuccess({
@@ -15240,7 +15250,7 @@ async function appsPullHandler(ctx) {
15240
15250
  humanSummary: `Pulled ${versionLabel} to ${pulled.projectDir}. Increment package.json notisAppVersion above the pulled release, then run \`cd ${pulled.projectDir} && npm install && notis apps dev\` to substitute the online bundle.`
15241
15251
  });
15242
15252
  }
15243
- function updateLinkedDeployState(projectDir, linkedState, appId, version, profileKey = null) {
15253
+ function updateLinkedDeployState(projectDir, linkedState, appId, version, profileKey = null, updatedAt = null) {
15244
15254
  if (!linkedState || linkedState.app_id !== appId || !Number.isFinite(version)) {
15245
15255
  return;
15246
15256
  }
@@ -15249,7 +15259,8 @@ function updateLinkedDeployState(projectDir, linkedState, appId, version, profil
15249
15259
  app_id: appId,
15250
15260
  version,
15251
15261
  linked_at: linkedState.linked_at || (/* @__PURE__ */ new Date()).toISOString(),
15252
- deployed_at: (/* @__PURE__ */ new Date()).toISOString()
15262
+ deployed_at: (/* @__PURE__ */ new Date()).toISOString(),
15263
+ expected_updated_at: updatedAt
15253
15264
  }, profileKey);
15254
15265
  }
15255
15266
  function resolveDeployTarget(ctx, projectDir) {
@@ -15310,6 +15321,7 @@ async function appsDeployHandler(ctx) {
15310
15321
  manifest,
15311
15322
  ...appRowFieldsFromManifest(manifest),
15312
15323
  ...baseVersion !== void 0 ? { base_version: baseVersion } : {},
15324
+ ...linkedState?.app_id === appId && linkedState?.expected_updated_at ? { expected_updated_at: linkedState.expected_updated_at } : {},
15313
15325
  ...needsPromotion ? { promote_dev_app: true } : {}
15314
15326
  },
15315
15327
  mutating: true,
@@ -15345,6 +15357,7 @@ async function appsDeployHandler(ctx) {
15345
15357
  app_id: appId,
15346
15358
  linked_at: linkedState?.linked_at || now,
15347
15359
  version: deployedVersion,
15360
+ expected_updated_at: result.payload.updated_at ?? null,
15348
15361
  deployed_at: now,
15349
15362
  dev_app_id: void 0,
15350
15363
  dev_linked_at: void 0
@@ -15365,7 +15378,7 @@ async function appsDeployHandler(ctx) {
15365
15378
  message: `Promoted development app to installed app ${appId}`
15366
15379
  });
15367
15380
  } else {
15368
- updateLinkedDeployState(projectDir, linkedState, appId, deployedVersion, profileKey);
15381
+ updateLinkedDeployState(projectDir, linkedState, appId, deployedVersion, profileKey, result.payload.updated_at);
15369
15382
  }
15370
15383
  return ctx.output.emitSuccess({
15371
15384
  command: ctx.spec.command_path.join(" "),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notis_ai/cli",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
4
4
  "description": "Agent-first Notis CLI for apps and generic tool execution",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1169,6 +1169,8 @@ async function appsCreateHandler(ctx) {
1169
1169
  if (projectDir) {
1170
1170
  writeLinkedState(projectDir, {
1171
1171
  app_id: app.id,
1172
+ version: deployedAppVersion(app),
1173
+ expected_updated_at: app.updated_at ?? null,
1172
1174
  linked_at: new Date().toISOString(),
1173
1175
  }, linkedStateProfileKey(ctx.runtime));
1174
1176
  }
@@ -2195,21 +2197,25 @@ async function appsLinkHandler(ctx) {
2195
2197
  const projectDir = resolveProjectDir(ctx.args.dir || '.');
2196
2198
  const appId = ctx.args.appId;
2197
2199
 
2198
- await assertLinkTarget(ctx.runtime, appId);
2199
-
2200
- writeLinkedState(
2201
- projectDir,
2202
- buildLinkedAppState(
2203
- readLinkedState(projectDir, linkedStateProfileKey(ctx.runtime)),
2204
- appId,
2205
- new Date().toISOString(),
2206
- ),
2207
- linkedStateProfileKey(ctx.runtime),
2208
- );
2200
+ const app = await assertLinkTarget(ctx.runtime, appId);
2201
+ const profileKey = linkedStateProfileKey(ctx.runtime);
2202
+ const previous = readLinkedState(projectDir, profileKey);
2203
+ const version = deployedAppVersion(app);
2204
+ if (previous?.app_id === appId && previous.version !== undefined && previous.version !== version) {
2205
+ throw usageError('The app release changed. Preserve local source, pull the current release into a fresh directory, and reapply changes before deploying.');
2206
+ }
2207
+ if (typeof app.updated_at !== 'string' || !app.updated_at.trim()) {
2208
+ throw usageError('App revision is unavailable; the directory was not relinked.');
2209
+ }
2210
+ writeLinkedState(projectDir, {
2211
+ ...buildLinkedAppState(previous, appId),
2212
+ version,
2213
+ expected_updated_at: app.updated_at,
2214
+ }, profileKey);
2209
2215
 
2210
2216
  return ctx.output.emitSuccess({
2211
2217
  command: ctx.spec.command_path.join(' '),
2212
- data: { app_id: appId, project_dir: projectDir },
2218
+ data: { app_id: appId, project_dir: projectDir, version, expected_updated_at: app.updated_at },
2213
2219
  humanSummary: `Linked to app ${appId}`,
2214
2220
  hints: [
2215
2221
  { command: 'notis apps deploy .', reason: 'Deploy the app' },
@@ -2251,6 +2257,7 @@ async function appsPullHandler(ctx) {
2251
2257
  version,
2252
2258
  force: Boolean(ctx.options.force),
2253
2259
  profileKey: linkedStateProfileKey(ctx.runtime),
2260
+ expectedUpdatedAt: app.updated_at,
2254
2261
  });
2255
2262
 
2256
2263
  const versionLabel = pulled.version === 'latest' ? 'latest version' : `v${pulled.version}`;
@@ -2265,7 +2272,7 @@ async function appsPullHandler(ctx) {
2265
2272
  });
2266
2273
  }
2267
2274
 
2268
- function updateLinkedDeployState(projectDir, linkedState, appId, version, profileKey = null) {
2275
+ function updateLinkedDeployState(projectDir, linkedState, appId, version, profileKey = null, updatedAt = null) {
2269
2276
  if (!linkedState || linkedState.app_id !== appId || !Number.isFinite(version)) {
2270
2277
  return;
2271
2278
  }
@@ -2275,6 +2282,7 @@ function updateLinkedDeployState(projectDir, linkedState, appId, version, profil
2275
2282
  version,
2276
2283
  linked_at: linkedState.linked_at || new Date().toISOString(),
2277
2284
  deployed_at: new Date().toISOString(),
2285
+ expected_updated_at: updatedAt,
2278
2286
  }, profileKey);
2279
2287
  }
2280
2288
 
@@ -2349,6 +2357,8 @@ async function appsDeployHandler(ctx) {
2349
2357
  manifest,
2350
2358
  ...appRowFieldsFromManifest(manifest),
2351
2359
  ...(baseVersion !== undefined ? { base_version: baseVersion } : {}),
2360
+ ...(linkedState?.app_id === appId && linkedState?.expected_updated_at
2361
+ ? { expected_updated_at: linkedState.expected_updated_at } : {}),
2352
2362
  ...(needsPromotion ? { promote_dev_app: true } : {}),
2353
2363
  },
2354
2364
  mutating: true,
@@ -2398,6 +2408,7 @@ async function appsDeployHandler(ctx) {
2398
2408
  app_id: appId,
2399
2409
  linked_at: linkedState?.linked_at || now,
2400
2410
  version: deployedVersion,
2411
+ expected_updated_at: result.payload.updated_at ?? null,
2401
2412
  deployed_at: now,
2402
2413
  dev_app_id: undefined,
2403
2414
  dev_linked_at: undefined,
@@ -2418,7 +2429,7 @@ async function appsDeployHandler(ctx) {
2418
2429
  message: `Promoted development app to installed app ${appId}`,
2419
2430
  });
2420
2431
  } else {
2421
- updateLinkedDeployState(projectDir, linkedState, appId, deployedVersion, profileKey);
2432
+ updateLinkedDeployState(projectDir, linkedState, appId, deployedVersion, profileKey, result.payload.updated_at);
2422
2433
  }
2423
2434
  return ctx.output.emitSuccess({
2424
2435
  command: ctx.spec.command_path.join(' '),
@@ -2415,6 +2415,7 @@ async function pullAppSourceUnlocked({
2415
2415
  version = 'latest',
2416
2416
  force = false,
2417
2417
  profileKey = null,
2418
+ expectedUpdatedAt = null,
2418
2419
  }, assertLockOwnership, assertParentIdentity, initialTargetIdentity) {
2419
2420
  assertLockOwnership();
2420
2421
  assertParentIdentity();
@@ -2580,6 +2581,7 @@ async function pullAppSourceUnlocked({
2580
2581
  writeLinkedState(targetRoot, {
2581
2582
  app_id: appId,
2582
2583
  ...(Number.isFinite(linkedVersion) ? { version: linkedVersion } : {}),
2584
+ expected_updated_at: expectedUpdatedAt,
2583
2585
  linked_at: new Date().toISOString(),
2584
2586
  }, profileKey);
2585
2587
  assertPullTargetIdentity(targetDir, activeTargetIdentity);
@@ -186,7 +186,7 @@ export function isEditableShortcutEvent(event: Event): boolean {
186
186
  return true;
187
187
  }
188
188
  if (tag === 'A' && target.hasAttribute('href')) return true;
189
- return ['button', 'link', 'menuitem', 'option', 'switch', 'tab'].includes(
189
+ return ['button', 'link', 'menuitem', 'switch', 'tab'].includes(
190
190
  target.getAttribute('role') || '',
191
191
  );
192
192
  });