@notis_ai/cli 0.2.0-beta.161.1 → 0.2.0-beta.164.1

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.
@@ -1355,7 +1355,7 @@ function connectedCallbackHtml({ portalOrigin }) {
1355
1355
  <button class="button button-primary" id="download-desktop" type="button">Download Notis Desktop</button>
1356
1356
  <a class="button" href="${escapeHtml(webAppUrl)}">Open Web App</a>
1357
1357
  </div>
1358
- <p class="helper">The CLI stays connected even if you continue in the web app.</p>
1358
+ <p class="helper">Desktop is available for macOS and Windows. The CLI stays connected even if you continue in the web app.</p>
1359
1359
  </section>
1360
1360
  <section class="next-step" id="desktop-ready-view" aria-labelledby="desktop-ready-title" hidden>
1361
1361
  <div class="status"><span class="status-dot"></span> Download started</div>
@@ -1381,7 +1381,6 @@ function connectedCallbackHtml({ portalOrigin }) {
1381
1381
  const resolveDownload = async () => {
1382
1382
  const ua = navigator.userAgent || '';
1383
1383
  if (/Windows/i.test(ua)) return downloads.base + '/win32/x64/notis-x64.exe';
1384
- if (/Linux/i.test(ua) && !/Android/i.test(ua)) return downloads.base + '/linux/x64/notis-linux-x64.zip';
1385
1384
  if (/Macintosh|Mac OS X/i.test(ua)) {
1386
1385
  let architecture = '';
1387
1386
  try {
@@ -1398,6 +1397,7 @@ function connectedCallbackHtml({ portalOrigin }) {
1398
1397
  const startDownload = async () => {
1399
1398
  const downloadUrl = await resolveDownload();
1400
1399
  window.open(downloadUrl, '_blank', 'noopener,noreferrer');
1400
+ if (downloadUrl === downloads.fallback) return;
1401
1401
  connectedView.hidden = true;
1402
1402
  readyView.hidden = false;
1403
1403
  document.title = 'Quick login to Notis Desktop';
@@ -4140,11 +4140,24 @@ async function syncSymlinks(skills, skillsDir = LEGACY_AGENTS_SKILLS_DIR, option
4140
4140
  }
4141
4141
  return result;
4142
4142
  }
4143
- function getPushCandidates(localSkills, syncState, cloudCuratedSkillNames = /* @__PURE__ */ new Set(), cloudSkillNames) {
4143
+ function selectCloudSkillsToApply(skills) {
4144
+ const winners = /* @__PURE__ */ new Map();
4145
+ for (const skill of skills) {
4146
+ const current = winners.get(skill.name);
4147
+ if (!current || (skill.updated_at || "") >= (current.updated_at || "")) {
4148
+ winners.set(skill.name, skill);
4149
+ }
4150
+ }
4151
+ return [...winners.values()];
4152
+ }
4153
+ function getPushCandidates(localSkills, syncState, cloudCuratedSkillNames = /* @__PURE__ */ new Set(), cloudSkillNames, cloudAppOwnedSkillNames = /* @__PURE__ */ new Set()) {
4144
4154
  return localSkills.filter((skill) => {
4145
4155
  if (cloudCuratedSkillNames.has(skill.name)) {
4146
4156
  return false;
4147
4157
  }
4158
+ if (cloudAppOwnedSkillNames.has(skill.name)) {
4159
+ return false;
4160
+ }
4148
4161
  const previous = syncState.skills[skill.name];
4149
4162
  if (!previous) {
4150
4163
  return true;
@@ -4238,17 +4251,28 @@ function shouldWriteCloudSkill(cloudSkill, localSkills, previousState) {
4238
4251
  }
4239
4252
  const previous = previousState.skills[skillName];
4240
4253
  if (previous?.folderHash === localSkill.folderHash && previous.cloudContentHash === cloudContentHash(cloudSkill)) return false;
4254
+ if (previous && previous.cloudContentHash === void 0 && previous.cloudId === cloudSkill.id && previous.appliedCloudFolderHash !== void 0 && previous.appliedCloudFolderHash === cloudHash && previous.cloudUpdatedAt === cloudSkill.updated_at && previous.folderHash === localSkill.folderHash) {
4255
+ return false;
4256
+ }
4241
4257
  if (cloudSkill.source === "curated") {
4242
4258
  return cloudHash ? cloudHash !== localSkill.folderHash : true;
4243
4259
  }
4244
4260
  const localChangedSinceLastSync = !previous || previous.folderHash !== localSkill.folderHash;
4245
4261
  return !localChangedSinceLastSync && (Boolean(cloudHash) && cloudHash !== localSkill.folderHash || Boolean(previous?.cloudContentHash && previous.cloudContentHash !== cloudContentHash(cloudSkill)));
4246
4262
  }
4247
- function buildSyncState(pullResponse, localSkills, lastSyncedAt, verifiedAgentLinks = {}, failedContentNames = /* @__PURE__ */ new Set()) {
4263
+ function collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillNames) {
4264
+ const applied = {};
4265
+ for (const skill of selectCloudSkillsToApply(pullResponse.skills)) {
4266
+ applied[skill.name] = writtenSkillNames.has(skill.name) ? skill.skill_folder_hash || "" : previousState.skills[skill.name]?.appliedCloudFolderHash;
4267
+ }
4268
+ return applied;
4269
+ }
4270
+ function buildSyncState(pullResponse, localSkills, lastSyncedAt, verifiedAgentLinks = {}, failedContentNames = /* @__PURE__ */ new Set(), appliedRevisions = {}) {
4248
4271
  const localSkillMap = toSkillMap(localSkills);
4249
4272
  const skills = Object.fromEntries(
4250
- pullResponse.skills.map((skill) => {
4273
+ selectCloudSkillsToApply(pullResponse.skills).map((skill) => {
4251
4274
  const localSkill = localSkillMap.get(skill.name);
4275
+ const appliedCloudFolderHash = appliedRevisions[skill.name];
4252
4276
  return [
4253
4277
  skill.name,
4254
4278
  {
@@ -4258,6 +4282,7 @@ function buildSyncState(pullResponse, localSkills, lastSyncedAt, verifiedAgentLi
4258
4282
  verifiedAgentLinks: skill.status === "active" ? verifiedAgentLinks[skill.name] ?? {} : {},
4259
4283
  cloudUpdatedAt: skill.updated_at,
4260
4284
  ...!failedContentNames.has(skill.name) && !skill.skill_source_url ? { cloudContentHash: cloudContentHash(skill) } : {},
4285
+ ...appliedCloudFolderHash !== void 0 ? { appliedCloudFolderHash } : {},
4261
4286
  syncedAt: lastSyncedAt || (/* @__PURE__ */ new Date()).toISOString()
4262
4287
  }
4263
4288
  ];
@@ -4319,7 +4344,7 @@ async function writePulledSkillsToScopedMirror(pullResponse, localSkills, previo
4319
4344
  console.warn(`[Notis] ${message}`, error);
4320
4345
  };
4321
4346
  let downloaded = 0;
4322
- for (const cloudSkill of pullResponse.skills) {
4347
+ for (const cloudSkill of selectCloudSkillsToApply(pullResponse.skills)) {
4323
4348
  if (!shouldWriteCloudSkill(cloudSkill, localSkillMap, previousState)) {
4324
4349
  continue;
4325
4350
  }
@@ -4400,7 +4425,8 @@ async function materializeCloudSkillsForLocalShell(serverUrl, jwt, dependencies
4400
4425
  finalLocalSkills,
4401
4426
  lastSyncedAt,
4402
4427
  verifiedLinks,
4403
- new Set(failedDownloads.map((item) => item.name))
4428
+ new Set(failedDownloads.map((item) => item.name)),
4429
+ collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillNames)
4404
4430
  );
4405
4431
  for (const [name, entry] of Object.entries(materializedState.skills)) {
4406
4432
  if (writtenSkillNames.has(name)) continue;
@@ -4555,7 +4581,8 @@ async function runSkillSync(serverUrl, jwt, dependencies = {}, options = {}) {
4555
4581
  localSkills,
4556
4582
  previousState,
4557
4583
  cloudCuratedSkillNames,
4558
- new Set(pullResponse.skills.map((skill) => skill.name))
4584
+ new Set(pullResponse.skills.map((skill) => skill.name)),
4585
+ new Set(pullResponse.skills.filter((skill) => skill.owner_app_id).map((skill) => skill.name))
4559
4586
  );
4560
4587
  const failedPushes = [];
4561
4588
  if (pushCandidates.length > 0) {
@@ -4579,13 +4606,15 @@ async function runSkillSync(serverUrl, jwt, dependencies = {}, options = {}) {
4579
4606
  }
4580
4607
  }
4581
4608
  const failedDownloads = [];
4609
+ const writtenSkillNames = /* @__PURE__ */ new Set();
4582
4610
  const downloaded = await writePulledSkillsToScopedMirror(
4583
4611
  pullResponse,
4584
4612
  localSkills,
4585
4613
  previousState,
4586
4614
  syncPaths,
4587
4615
  deps,
4588
- failedDownloads
4616
+ failedDownloads,
4617
+ writtenSkillNames
4589
4618
  );
4590
4619
  const finalLocalSkills = (await deps.scanLocalSkills(syncPaths)).filter((skill) => !BASE_SKILL_NAMES2.has(skill.name));
4591
4620
  const symlinkResult = await deps.syncSymlinks(
@@ -4596,7 +4625,14 @@ async function runSkillSync(serverUrl, jwt, dependencies = {}, options = {}) {
4596
4625
  for (const failure of failedDownloads) delete verifiedLinks[failure.name];
4597
4626
  const lastSyncedAt = pullResponse.last_synced_at || (/* @__PURE__ */ new Date()).toISOString();
4598
4627
  await deps.writeSyncState(
4599
- buildSyncState(pullResponse, finalLocalSkills, lastSyncedAt, verifiedLinks, new Set(failedDownloads.map((item) => item.name))),
4628
+ buildSyncState(
4629
+ pullResponse,
4630
+ finalLocalSkills,
4631
+ lastSyncedAt,
4632
+ verifiedLinks,
4633
+ new Set(failedDownloads.map((item) => item.name)),
4634
+ collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillNames)
4635
+ ),
4600
4636
  syncPaths
4601
4637
  );
4602
4638
  return {
@@ -9027,7 +9063,7 @@ function resolveBuiltBundleDir(projectDir) {
9027
9063
  return null;
9028
9064
  }
9029
9065
  function normalizeShadowScopedCss(css) {
9030
- return css.replace(/html\s*,\s*:host\s*\{/g, ":host{").replace(/:root\s*,\s*:host\s*\{/g, ":host{").replace(/:root\s*\{/g, ":host{").replace(/html\s*\{/g, ":host{").replace(/body\s*\{/g, "[data-notis-app-root]{");
9066
+ return css.replace(/(?:html|:root)\s*,\s*:host\s*\{/g, "[data-notis-app-root],:host{").replace(/:root\s*\{/g, "[data-notis-app-root],:host{").replace(/html\s*\{/g, "[data-notis-app-root],:host{").replace(/body\s*\{/g, "[data-notis-app-root]{");
9031
9067
  }
9032
9068
  function normalizeBundleStylesheets(projectDir) {
9033
9069
  const bundleDir = join4(projectDir, BUNDLE_DIR);
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: notis-cli
3
3
  description: Use when agents should work through the Notis CLI, especially to develop Notis apps locally or to access Notis, Composio, or MCP tools they do not currently have loaded directly.
4
- feature_flag: cli_access
5
4
  mcp_resource: true
6
5
  mcp_tool_patterns: []
7
6
  mcp_references: ["references/app-delivery.md", "references/tool-examples.md", "references/native-databases.md", "references/troubleshooting.md"]
@@ -1118,11 +1118,24 @@ async function syncSymlinks(skills, skillsDir = LEGACY_AGENTS_SKILLS_DIR, option
1118
1118
  }
1119
1119
 
1120
1120
  // src/runtime/skill-sync/sync-plan.ts
1121
- function getPushCandidates(localSkills, syncState, cloudCuratedSkillNames = /* @__PURE__ */ new Set(), cloudSkillNames) {
1121
+ function selectCloudSkillsToApply(skills) {
1122
+ const winners = /* @__PURE__ */ new Map();
1123
+ for (const skill of skills) {
1124
+ const current = winners.get(skill.name);
1125
+ if (!current || (skill.updated_at || "") >= (current.updated_at || "")) {
1126
+ winners.set(skill.name, skill);
1127
+ }
1128
+ }
1129
+ return [...winners.values()];
1130
+ }
1131
+ function getPushCandidates(localSkills, syncState, cloudCuratedSkillNames = /* @__PURE__ */ new Set(), cloudSkillNames, cloudAppOwnedSkillNames = /* @__PURE__ */ new Set()) {
1122
1132
  return localSkills.filter((skill) => {
1123
1133
  if (cloudCuratedSkillNames.has(skill.name)) {
1124
1134
  return false;
1125
1135
  }
1136
+ if (cloudAppOwnedSkillNames.has(skill.name)) {
1137
+ return false;
1138
+ }
1126
1139
  const previous = syncState.skills[skill.name];
1127
1140
  if (!previous) {
1128
1141
  return true;
@@ -1239,17 +1252,28 @@ function shouldWriteCloudSkill(cloudSkill, localSkills, previousState) {
1239
1252
  }
1240
1253
  const previous = previousState.skills[skillName];
1241
1254
  if (previous?.folderHash === localSkill.folderHash && previous.cloudContentHash === cloudContentHash(cloudSkill)) return false;
1255
+ if (previous && previous.cloudContentHash === void 0 && previous.cloudId === cloudSkill.id && previous.appliedCloudFolderHash !== void 0 && previous.appliedCloudFolderHash === cloudHash && previous.cloudUpdatedAt === cloudSkill.updated_at && previous.folderHash === localSkill.folderHash) {
1256
+ return false;
1257
+ }
1242
1258
  if (cloudSkill.source === "curated") {
1243
1259
  return cloudHash ? cloudHash !== localSkill.folderHash : true;
1244
1260
  }
1245
1261
  const localChangedSinceLastSync = !previous || previous.folderHash !== localSkill.folderHash;
1246
1262
  return !localChangedSinceLastSync && (Boolean(cloudHash) && cloudHash !== localSkill.folderHash || Boolean(previous?.cloudContentHash && previous.cloudContentHash !== cloudContentHash(cloudSkill)));
1247
1263
  }
1248
- function buildSyncState(pullResponse, localSkills, lastSyncedAt, verifiedAgentLinks = {}, failedContentNames = /* @__PURE__ */ new Set()) {
1264
+ function collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillNames) {
1265
+ const applied = {};
1266
+ for (const skill of selectCloudSkillsToApply(pullResponse.skills)) {
1267
+ applied[skill.name] = writtenSkillNames.has(skill.name) ? skill.skill_folder_hash || "" : previousState.skills[skill.name]?.appliedCloudFolderHash;
1268
+ }
1269
+ return applied;
1270
+ }
1271
+ function buildSyncState(pullResponse, localSkills, lastSyncedAt, verifiedAgentLinks = {}, failedContentNames = /* @__PURE__ */ new Set(), appliedRevisions = {}) {
1249
1272
  const localSkillMap = toSkillMap(localSkills);
1250
1273
  const skills = Object.fromEntries(
1251
- pullResponse.skills.map((skill) => {
1274
+ selectCloudSkillsToApply(pullResponse.skills).map((skill) => {
1252
1275
  const localSkill = localSkillMap.get(skill.name);
1276
+ const appliedCloudFolderHash = appliedRevisions[skill.name];
1253
1277
  return [
1254
1278
  skill.name,
1255
1279
  {
@@ -1259,6 +1283,7 @@ function buildSyncState(pullResponse, localSkills, lastSyncedAt, verifiedAgentLi
1259
1283
  verifiedAgentLinks: skill.status === "active" ? verifiedAgentLinks[skill.name] ?? {} : {},
1260
1284
  cloudUpdatedAt: skill.updated_at,
1261
1285
  ...!failedContentNames.has(skill.name) && !skill.skill_source_url ? { cloudContentHash: cloudContentHash(skill) } : {},
1286
+ ...appliedCloudFolderHash !== void 0 ? { appliedCloudFolderHash } : {},
1262
1287
  syncedAt: lastSyncedAt || (/* @__PURE__ */ new Date()).toISOString()
1263
1288
  }
1264
1289
  ];
@@ -1320,7 +1345,7 @@ async function writePulledSkillsToScopedMirror(pullResponse, localSkills, previo
1320
1345
  console.warn(`[Notis] ${message}`, error);
1321
1346
  };
1322
1347
  let downloaded = 0;
1323
- for (const cloudSkill of pullResponse.skills) {
1348
+ for (const cloudSkill of selectCloudSkillsToApply(pullResponse.skills)) {
1324
1349
  if (!shouldWriteCloudSkill(cloudSkill, localSkillMap, previousState)) {
1325
1350
  continue;
1326
1351
  }
@@ -1401,7 +1426,8 @@ async function materializeCloudSkillsForLocalShell(serverUrl, jwt, dependencies
1401
1426
  finalLocalSkills,
1402
1427
  lastSyncedAt,
1403
1428
  verifiedLinks,
1404
- new Set(failedDownloads.map((item) => item.name))
1429
+ new Set(failedDownloads.map((item) => item.name)),
1430
+ collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillNames)
1405
1431
  );
1406
1432
  for (const [name, entry] of Object.entries(materializedState.skills)) {
1407
1433
  if (writtenSkillNames.has(name)) continue;
@@ -1556,7 +1582,8 @@ async function runSkillSync(serverUrl, jwt, dependencies = {}, options = {}) {
1556
1582
  localSkills,
1557
1583
  previousState,
1558
1584
  cloudCuratedSkillNames,
1559
- new Set(pullResponse.skills.map((skill) => skill.name))
1585
+ new Set(pullResponse.skills.map((skill) => skill.name)),
1586
+ new Set(pullResponse.skills.filter((skill) => skill.owner_app_id).map((skill) => skill.name))
1560
1587
  );
1561
1588
  const failedPushes = [];
1562
1589
  if (pushCandidates.length > 0) {
@@ -1580,13 +1607,15 @@ async function runSkillSync(serverUrl, jwt, dependencies = {}, options = {}) {
1580
1607
  }
1581
1608
  }
1582
1609
  const failedDownloads = [];
1610
+ const writtenSkillNames = /* @__PURE__ */ new Set();
1583
1611
  const downloaded = await writePulledSkillsToScopedMirror(
1584
1612
  pullResponse,
1585
1613
  localSkills,
1586
1614
  previousState,
1587
1615
  syncPaths,
1588
1616
  deps,
1589
- failedDownloads
1617
+ failedDownloads,
1618
+ writtenSkillNames
1590
1619
  );
1591
1620
  const finalLocalSkills = (await deps.scanLocalSkills(syncPaths)).filter((skill) => !BASE_SKILL_NAMES.has(skill.name));
1592
1621
  const symlinkResult = await deps.syncSymlinks(
@@ -1597,7 +1626,14 @@ async function runSkillSync(serverUrl, jwt, dependencies = {}, options = {}) {
1597
1626
  for (const failure of failedDownloads) delete verifiedLinks[failure.name];
1598
1627
  const lastSyncedAt = pullResponse.last_synced_at || (/* @__PURE__ */ new Date()).toISOString();
1599
1628
  await deps.writeSyncState(
1600
- buildSyncState(pullResponse, finalLocalSkills, lastSyncedAt, verifiedLinks, new Set(failedDownloads.map((item) => item.name))),
1629
+ buildSyncState(
1630
+ pullResponse,
1631
+ finalLocalSkills,
1632
+ lastSyncedAt,
1633
+ verifiedLinks,
1634
+ new Set(failedDownloads.map((item) => item.name)),
1635
+ collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillNames)
1636
+ ),
1601
1637
  syncPaths
1602
1638
  );
1603
1639
  return {