@north-light/cli 0.3.458 → 0.3.460

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 (43) hide show
  1. package/dist/api/contract.d.ts +1 -1
  2. package/dist/api/contract.js +1 -1
  3. package/dist/api/dto/profiles.d.ts +3 -1
  4. package/dist/builtin-memory/crouter-sdk/guides/apps-on-northlight.md +1 -1
  5. package/dist/builtin-memory/crouter-sdk/release-notes.md +12 -0
  6. package/dist/clients/attach/session/profile-files.js +1 -1
  7. package/dist/clients/attach/viewer.js +178 -178
  8. package/dist/commands/profile/new.js +1 -1
  9. package/dist/commands/profile/show.js +1 -1
  10. package/dist/commands/sys/join.js +1 -1
  11. package/dist/commands/sys/panels/models-panel.js +1 -1
  12. package/dist/core/command-plugins/compose.js +1 -1
  13. package/dist/core/config.js +1 -1
  14. package/dist/core/directory/join.d.ts +4 -2
  15. package/dist/core/directory/join.js +1 -1
  16. package/dist/core/io.d.ts +12 -2
  17. package/dist/core/io.js +7 -7
  18. package/dist/core/layout-migrate/stored-paths.js +3 -3
  19. package/dist/core/layout-migrate/stores.js +1 -1
  20. package/dist/core/layout.d.ts +2 -2
  21. package/dist/core/layout.js +2 -2
  22. package/dist/core/profiles/manifest.d.ts +1 -1
  23. package/dist/core/profiles/manifest.js +1 -1
  24. package/dist/core/profiles/relocate.d.ts +8 -0
  25. package/dist/core/profiles/relocate.js +1 -0
  26. package/dist/core/providers/device.js +1 -1
  27. package/dist/core/render.d.ts +1 -1
  28. package/dist/core/render.js +6 -6
  29. package/dist/core/scoped-state/migrate.js +1 -1
  30. package/dist/core/scoped-state/profiles.js +1 -1
  31. package/dist/core/scoped-state/settings.js +1 -1
  32. package/dist/core/spaces/space.js +2 -2
  33. package/dist/core/storage/download.js +1 -1
  34. package/dist/daemon/api/map.js +2 -2
  35. package/dist/daemon/api/router.js +1 -1
  36. package/dist/daemon/crtrd.js +8 -7
  37. package/dist/migrations/004-canvas-documents/index.js +2 -2
  38. package/dist/migrations/004-canvas-documents/legacy/types.d.ts +1 -1
  39. package/docs/sdk/guides/apps-on-northlight.md +1 -1
  40. package/docs/sdk/release-notes.md +12 -0
  41. package/package.json +2 -2
  42. package/packages/crouter-identity/package.json +1 -1
  43. package/runtime.lock.json +12 -12
@@ -1,5 +1,5 @@
1
1
  /** The contract level this build serves and requires. Written by `scripts/contract.mjs release`; never edit by hand. */
2
- export declare const CONTRACT_LEVEL = "1.4";
2
+ export declare const CONTRACT_LEVEL = "1.5";
3
3
  /** Request header naming the one contract level the caller needs, e.g. `1.3`. Optional: absent means no check. */
4
4
  export declare const REQUIRED_CONTRACT_HEADER = "Crouter-Required-Contract";
5
5
  /** Response header listing the contract levels the server serves, comma-separated, one per major, e.g. `1.3`. */
@@ -1 +1 @@
1
- var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.4",i="Crouter-Required-Contract",p="Crouter-Contract";function l(r){const t=/^(\d+)\.(\d+)$/.exec(r.trim());return t===null?null:{major:Number(t[1]),minor:Number(t[2])}}e(l,"parseContractLevel");function a(r){return r==null?[]:r.split(",").map(t=>l(t)).filter(t=>t!==null)}e(a,"parseServedContracts");function s(r,t){const n=l(r);return n===null?!1:(typeof t=="string"||t===null||t===void 0?a(t):t).some(u=>u.major===n.major&&u.minor>=n.minor)}e(s,"contractSatisfies");function C(r,t,n={}){if(r===void 0)return null;if(l(r)===null)return{status:400,code:"invalid_request",param:i,message:`${i} must be a contract level like 1.0.`};if(s(r,t))return null;const o=r.trim();return{status:426,code:"runtime_version_unsupported",message:t===null||a(t).length===0?`The runtime did not name its contract level; the caller requires contract ${o} or later. Roll the runtime forward.`:`The runtime serves contract ${t}; the caller requires contract ${o} or later within the same major. Roll the runtime forward, or pin an SDK whose crouterContract the runtime serves.`,details:{sdk_version:(n.legacyMinimum??"").trim(),runtime_version:n.runtimeVersion??null,required_contract:o,runtime_contract:t}}}e(C,"contractRefusal");export{p as CONTRACT_HEADER,f as CONTRACT_LEVEL,i as REQUIRED_CONTRACT_HEADER,C as contractRefusal,s as contractSatisfies,l as parseContractLevel,a as parseServedContracts};
1
+ var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.5",i="Crouter-Required-Contract",p="Crouter-Contract";function l(r){const t=/^(\d+)\.(\d+)$/.exec(r.trim());return t===null?null:{major:Number(t[1]),minor:Number(t[2])}}e(l,"parseContractLevel");function a(r){return r==null?[]:r.split(",").map(t=>l(t)).filter(t=>t!==null)}e(a,"parseServedContracts");function s(r,t){const n=l(r);return n===null?!1:(typeof t=="string"||t===null||t===void 0?a(t):t).some(u=>u.major===n.major&&u.minor>=n.minor)}e(s,"contractSatisfies");function C(r,t,n={}){if(r===void 0)return null;if(l(r)===null)return{status:400,code:"invalid_request",param:i,message:`${i} must be a contract level like 1.0.`};if(s(r,t))return null;const o=r.trim();return{status:426,code:"runtime_version_unsupported",message:t===null||a(t).length===0?`The runtime did not name its contract level; the caller requires contract ${o} or later. Roll the runtime forward.`:`The runtime serves contract ${t}; the caller requires contract ${o} or later within the same major. Roll the runtime forward, or pin an SDK whose crouterContract the runtime serves.`,details:{sdk_version:(n.legacyMinimum??"").trim(),runtime_version:n.runtimeVersion??null,required_contract:o,runtime_contract:t}}}e(C,"contractRefusal");export{p as CONTRACT_HEADER,f as CONTRACT_LEVEL,i as REQUIRED_CONTRACT_HEADER,C as contractRefusal,s as contractSatisfies,l as parseContractLevel,a as parseServedContracts};
@@ -112,6 +112,8 @@ export interface ProfileDTO {
112
112
  created_at: string;
113
113
  /** ISO timestamp of the most recent selection, or null if never selected. */
114
114
  last_used_at: string | null;
115
- /** Absolute path of the profile's directory under its app's space (the legacy location of its pre-canvas markdown; the profile's documents live in the canvas). */
115
+ /** Absolute path of the profile's directory, `<app space>/profiles/<id>`: its settings root. The profile's documents live in the canvas, not here. */
116
+ profile_path: string;
117
+ /** @deprecated The same path as `profile_path`; kept for clients written before it existed. Removed in the next breaking release. */
116
118
  memory_path: string;
117
119
  }
@@ -36,7 +36,7 @@ Then join your laptop to the account:
36
36
  crtr sys join --directory https://account.northlight.ai --token <token> --name my-laptop
37
37
  ```
38
38
 
39
- It answers with `registered: true`, the machine id (`runtime:<id>`), your user id and `daemon: restarted` (`daemon: not_running` when crtr was not running; the next crtr command starts it). Your open viewers and nodes reconnect on their own.
39
+ It answers with `registered: true`, the machine id (`runtime:<id>`), your user id and `daemon: restarted` (`daemon: started` when crtr was not running: join starts the daemon, and its first grant sync installs `crtr cloud …` and your other provider plugins). Your open viewers and nodes reconnect on their own.
40
40
  3. Check it: `crtr cloud apps mine` lists the apps you manage. Before you have joined, it answers `not_joined` and prints these same steps.
41
41
 
42
42
  Things to know before you join:
@@ -9,6 +9,18 @@ when-and-why-to-read: When you need Release notes, this knowledge should be read
9
9
 
10
10
  Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
11
11
 
12
+ ## 0.3.460
13
+
14
+ ### `crouter-sdk keygen` releases its key file when the add fails (no app change)
15
+
16
+ When `keygen` cannot add the public key to the app (the machine is not joined, or the directory refuses), it deletes the `local/app-key.json` it had reserved, so a rerun after fixing the cause starts clean. It keeps the file when the add timed out or returned no key, because the add may have landed.
17
+
18
+ ## 0.3.459
19
+
20
+ ### `ProfileDTO.profile_path` replaces `memory_path` (additive, no app change)
21
+
22
+ A profile's folder holds no documents (those live in the canvas), so it is now `<app space>/profiles/<id>` rather than `<app space>/memory/profiles/<id>`, and the profile objects carry `profile_path` with that path. `memory_path` is still sent with the same value and is deprecated; it goes in the next breaking release, so read `profile_path` and fall back to `memory_path` against an older runtime. Nothing else changes: the runtime moves existing folders itself on startup.
23
+
12
24
  ## 0.3.455
13
25
 
14
26
  ### `provider.call` can target a device (additive, no app change)
@@ -1 +1 @@
1
- var N=Object.defineProperty;var l=(e,t)=>N(e,"name",{value:t,configurable:!0});import{constants as _,accessSync as j,existsSync as P,mkdtempSync as k,readFileSync as E,rmSync as d,statSync as z}from"node:fs";import{tmpdir as C}from"node:os";import{delimiter as F,join as p,resolve as I,sep as $}from"node:path";import{execFile as v,spawnSync as T}from"node:child_process";import{loadProfileManifest as q,profileMemoryDir as M}from"../../../core/profiles/manifest.js";import{contextDir as O}from"../../../core/canvas/node-agent-paths.js";import{shellQuote as c}from"../../../core/runtime/placement-tmux.js";import{nvimShellCommand as A}from"../../../core/nvim-launch.js";import{surfaceTmuxStyleArgs as S}from"../../../core/runtime/surface-bg.js";function H(e,t){const n=new Map(e.map(i=>[i.node_id,i])),r=new Set([t]);for(let i=n.get(t);i?.parent!==null&&i?.parent!==void 0;){const f=i.parent;if(r.has(f))break;r.add(f),i=n.get(f)}const s=[t];for(;s.length>0;){const i=s.pop();for(const f of e)f.parent!==i||r.has(f.node_id)||(r.add(f.node_id),s.push(f.node_id))}return e.filter(i=>r.has(i.node_id)).map(i=>i.node_id)}l(H,"relatedNodeIds");function ce(e,t){const n=new Set(H(e,t));return e.filter(r=>n.has(r.node_id)).map(r=>r.cwd)}l(ce,"relatedNodeDirs");function L(e){const t=[];for(const n of e)try{t.push(O(n))}catch{continue}return y(t)}l(L,"familyDocumentDirs");function R(e){try{return z(e).isDirectory()}catch{return!1}}l(R,"isDirectory");function W(e,t){const n=[...t==null?[]:[t],...(process.env.PATH??"").split(F).filter(Boolean).map(r=>p(r,e))];return[...new Set(n)]}l(W,"executableCandidates");function h(e,t){for(const n of W(e,t)){try{j(n,_.X_OK)}catch{continue}if(T(n,["--version"],{stdio:"ignore"}).status===0)return n}return null}l(h,"workingExecutable");function B(e){if(e===null||e==="")return{projects:[],memory:null};try{const{profileId:t,manifest:n}=q(e);return{projects:n.projects.map(r=>r.path),memory:M(t)}}catch{return{projects:[],memory:null}}}l(B,"profileSearchRoots");function y(e){const t=new Set,n=[];for(const r of e){const s=I(r);t.has(s)||!R(s)||(t.add(s),n.push(s))}return n}l(y,"dedupeExistingDirs");function K(e){return e.filter(t=>!e.some(n=>n!==t&&t.startsWith(n+$)))}l(K,"outermostDirs");const Q=20;function X(e){try{const t=E(e);if(t.length===0)return null;const n=t[t.length-1]===0?t.length-1:t.length;return t.subarray(0,n).toString("utf8")||null}catch{return null}}l(X,"selectionPath");function se(e){const t=h("fd",e.fdPath);if(t===null){e.onNotice("fd is required for profile file search");return}const n=h("fzf");if(n===null){e.onNotice("fzf is required for profile file search");return}const r=h("nvim");if(r===null){e.onNotice("Neovim is required for profile file search");return}const s=B(e.profileId),i=y([e.cwd,...e.nodeDirs,...s.projects]);if(i.length===0){e.onNotice("No profile file-search directories exist");return}const f=K(i),w=y([...s.memory===null?[]:[s.memory]]),u=k(p(C(),"crtr-profile-files-")),g=p(u,"selection"),x=p(u,"candidates"),b=L(e.nodeIds).filter(o=>!f.some(a=>o===a||o.startsWith(a+$))),D=`{ ${[...f.map(o=>`${c(t)} --type f --hidden --exclude .git . ${c(o)}`),...b.map(o=>`${c(t)} --type f --hidden --exclude .git --exclude ${c("/jobs")} . ${c(o)}`),...w.map(o=>`${c(t)} --type f --hidden --exclude .git --no-ignore . ${c(o)}`)].map(o=>`${o} &`).join(" ")} wait; } > ${c(x)} && ${c(n)} --print0 --scheme=path --hscroll-off=${Q} --header ${c("fuzzy path \u2022 ctrl-y yank file path")} --bind ${c("ctrl-y:execute-silent(printf '%s' {} | { if command -v pbcopy >/dev/null 2>&1; then pbcopy; elif command -v wl-copy >/dev/null 2>&1; then wl-copy; elif command -v xclip >/dev/null 2>&1; then xclip -selection clipboard; else cat >/dev/null; fi; }; printf '%s' {} | tmux load-buffer -w -)+abort")} --preview ${c("head -100 -- {}")} < ${c(x)} > ${c(g)}`;try{v("tmux",["display-popup","-E","-T"," profile files ","-w","80%","-h","60%",...S(),D],()=>{const o=X(g);if(o===null||!P(o)){d(u,{recursive:!0,force:!0});return}const a=`exec ${A("edit",[o],r)}`;try{v("tmux",["display-popup","-E","-T"," profile file ","-w","90%","-h","90%",...S(),a],()=>d(u,{recursive:!0,force:!0}))}catch(m){d(u,{recursive:!0,force:!0}),e.onNotice(`Could not open profile file: ${m instanceof Error?m.message:String(m)}`)}})}catch(o){d(u,{recursive:!0,force:!0}),e.onNotice(`Could not open profile file search: ${o instanceof Error?o.message:String(o)}`)}}l(se,"launchProfileFileSearch");export{se as launchProfileFileSearch,ce as relatedNodeDirs,H as relatedNodeIds};
1
+ var N=Object.defineProperty;var l=(e,t)=>N(e,"name",{value:t,configurable:!0});import{constants as _,accessSync as j,existsSync as P,mkdtempSync as k,readFileSync as E,rmSync as d,statSync as z}from"node:fs";import{tmpdir as C}from"node:os";import{delimiter as F,join as p,resolve as I,sep as $}from"node:path";import{execFile as v,spawnSync as T}from"node:child_process";import{loadProfileManifest as q,profileDir as O}from"../../../core/profiles/manifest.js";import{contextDir as A}from"../../../core/canvas/node-agent-paths.js";import{shellQuote as c}from"../../../core/runtime/placement-tmux.js";import{nvimShellCommand as H}from"../../../core/nvim-launch.js";import{surfaceTmuxStyleArgs as S}from"../../../core/runtime/surface-bg.js";function L(e,t){const n=new Map(e.map(o=>[o.node_id,o])),r=new Set([t]);for(let o=n.get(t);o?.parent!==null&&o?.parent!==void 0;){const f=o.parent;if(r.has(f))break;r.add(f),o=n.get(f)}const s=[t];for(;s.length>0;){const o=s.pop();for(const f of e)f.parent!==o||r.has(f.node_id)||(r.add(f.node_id),s.push(f.node_id))}return e.filter(o=>r.has(o.node_id)).map(o=>o.node_id)}l(L,"relatedNodeIds");function ce(e,t){const n=new Set(L(e,t));return e.filter(r=>n.has(r.node_id)).map(r=>r.cwd)}l(ce,"relatedNodeDirs");function M(e){const t=[];for(const n of e)try{t.push(A(n))}catch{continue}return y(t)}l(M,"familyDocumentDirs");function R(e){try{return z(e).isDirectory()}catch{return!1}}l(R,"isDirectory");function W(e,t){const n=[...t==null?[]:[t],...(process.env.PATH??"").split(F).filter(Boolean).map(r=>p(r,e))];return[...new Set(n)]}l(W,"executableCandidates");function h(e,t){for(const n of W(e,t)){try{j(n,_.X_OK)}catch{continue}if(T(n,["--version"],{stdio:"ignore"}).status===0)return n}return null}l(h,"workingExecutable");function B(e){if(e===null||e==="")return{projects:[],settings:null};try{const{profileId:t,manifest:n}=q(e);return{projects:n.projects.map(r=>r.path),settings:O(t)}}catch{return{projects:[],settings:null}}}l(B,"profileSearchRoots");function y(e){const t=new Set,n=[];for(const r of e){const s=I(r);t.has(s)||!R(s)||(t.add(s),n.push(s))}return n}l(y,"dedupeExistingDirs");function K(e){return e.filter(t=>!e.some(n=>n!==t&&t.startsWith(n+$)))}l(K,"outermostDirs");const Q=20;function X(e){try{const t=E(e);if(t.length===0)return null;const n=t[t.length-1]===0?t.length-1:t.length;return t.subarray(0,n).toString("utf8")||null}catch{return null}}l(X,"selectionPath");function se(e){const t=h("fd",e.fdPath);if(t===null){e.onNotice("fd is required for profile file search");return}const n=h("fzf");if(n===null){e.onNotice("fzf is required for profile file search");return}const r=h("nvim");if(r===null){e.onNotice("Neovim is required for profile file search");return}const s=B(e.profileId),o=y([e.cwd,...e.nodeDirs,...s.projects]);if(o.length===0){e.onNotice("No profile file-search directories exist");return}const f=K(o),w=y([...s.settings===null?[]:[s.settings]]),u=k(p(C(),"crtr-profile-files-")),g=p(u,"selection"),x=p(u,"candidates"),b=M(e.nodeIds).filter(i=>!f.some(a=>i===a||i.startsWith(a+$))),D=`{ ${[...f.map(i=>`${c(t)} --type f --hidden --exclude .git . ${c(i)}`),...b.map(i=>`${c(t)} --type f --hidden --exclude .git --exclude ${c("/jobs")} . ${c(i)}`),...w.map(i=>`${c(t)} --type f --hidden --exclude .git --no-ignore . ${c(i)}`)].map(i=>`${i} &`).join(" ")} wait; } > ${c(x)} && ${c(n)} --print0 --scheme=path --hscroll-off=${Q} --header ${c("fuzzy path \u2022 ctrl-y yank file path")} --bind ${c("ctrl-y:execute-silent(printf '%s' {} | { if command -v pbcopy >/dev/null 2>&1; then pbcopy; elif command -v wl-copy >/dev/null 2>&1; then wl-copy; elif command -v xclip >/dev/null 2>&1; then xclip -selection clipboard; else cat >/dev/null; fi; }; printf '%s' {} | tmux load-buffer -w -)+abort")} --preview ${c("head -100 -- {}")} < ${c(x)} > ${c(g)}`;try{v("tmux",["display-popup","-E","-T"," profile files ","-w","80%","-h","60%",...S(),D],()=>{const i=X(g);if(i===null||!P(i)){d(u,{recursive:!0,force:!0});return}const a=`exec ${H("edit",[i],r)}`;try{v("tmux",["display-popup","-E","-T"," profile file ","-w","90%","-h","90%",...S(),a],()=>d(u,{recursive:!0,force:!0}))}catch(m){d(u,{recursive:!0,force:!0}),e.onNotice(`Could not open profile file: ${m instanceof Error?m.message:String(m)}`)}})}catch(i){d(u,{recursive:!0,force:!0}),e.onNotice(`Could not open profile file search: ${i instanceof Error?i.message:String(i)}`)}}l(se,"launchProfileFileSearch");export{se as launchProfileFileSearch,ce as relatedNodeDirs,L as relatedNodeIds};