@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.
- package/dist/agent-hooks/notis-agent-hook.mjs +47 -11
- package/dist/base-skills/notis-cli/SKILL.md +0 -1
- package/dist/skill-sync/index.js +44 -8
- package/dist/skill-sync/index.js.map +2 -2
- package/dist/skill-sync-worker.mjs +42 -7
- package/package.json +1 -1
- package/src/runtime/app-platform.js +6 -6
- package/src/runtime/oauth.js +2 -2
- package/src/runtime/skill-sync/index.ts +59 -4
- package/src/runtime/skill-sync/sync-plan.ts +26 -1
- package/src/runtime/skill-sync/types.ts +8 -0
- package/template/.harness/index.html.tmpl +1 -1
|
@@ -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
|
|
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
|
|
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(
|
|
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, "
|
|
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"]
|
package/dist/skill-sync/index.js
CHANGED
|
@@ -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
|
|
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
|
|
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(
|
|
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 {
|