@halcyonlabs/app 0.47.0-halcyon.344 → 0.47.0-halcyon.346

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/index.d.ts CHANGED
@@ -4329,8 +4329,8 @@ declare const threadTimelinePendingTodosSchema: z$1.ZodObject<{
4329
4329
  type ThreadTimelinePendingTodos = z$1.infer<typeof threadTimelinePendingTodosSchema>;
4330
4330
 
4331
4331
  declare const threadParentNotificationsSchema: z$1.ZodEnum<{
4332
- auto: "auto";
4333
4332
  off: "off";
4333
+ auto: "auto";
4334
4334
  stuck: "stuck";
4335
4335
  }>;
4336
4336
  type ThreadParentNotifications = z$1.infer<typeof threadParentNotificationsSchema>;
@@ -6129,8 +6129,8 @@ declare const hostDaemonCommandRegistry: {
6129
6129
  skillFilePath: z$1.ZodString;
6130
6130
  }, z$1.core.$strict>], "kind">>;
6131
6131
  instructionMode: z$1.ZodEnum<{
6132
- replace: "replace";
6133
6132
  append: "append";
6133
+ replace: "replace";
6134
6134
  }>;
6135
6135
  type: z$1.ZodLiteral<"thread.rewind.prepare">;
6136
6136
  leaseId: z$1.ZodString;
@@ -6278,8 +6278,8 @@ declare const hostDaemonCommandRegistry: {
6278
6278
  skillFilePath: z$1.ZodString;
6279
6279
  }, z$1.core.$strict>], "kind">>;
6280
6280
  instructionMode: z$1.ZodEnum<{
6281
- replace: "replace";
6282
6281
  append: "append";
6282
+ replace: "replace";
6283
6283
  }>;
6284
6284
  type: z$1.ZodLiteral<"thread.start">;
6285
6285
  requestId: z$1.ZodString;
@@ -6742,8 +6742,8 @@ declare const hostDaemonCommandRegistry: {
6742
6742
  envPassthrough: z$1.ZodArray<z$1.ZodString>;
6743
6743
  }, z$1.core.$strict>;
6744
6744
  instructionMode: z$1.ZodEnum<{
6745
- replace: "replace";
6746
6745
  append: "append";
6746
+ replace: "replace";
6747
6747
  }>;
6748
6748
  projectId: z$1.ZodString;
6749
6749
  providerId: z$1.ZodString;
@@ -6958,8 +6958,8 @@ declare const hostDaemonCommandRegistry: {
6958
6958
  envPassthrough: z$1.ZodArray<z$1.ZodString>;
6959
6959
  }, z$1.core.$strict>;
6960
6960
  instructionMode: z$1.ZodEnum<{
6961
- replace: "replace";
6962
6961
  append: "append";
6962
+ replace: "replace";
6963
6963
  }>;
6964
6964
  projectId: z$1.ZodString;
6965
6965
  providerId: z$1.ZodString;
@@ -7865,9 +7865,9 @@ declare const hostDaemonCommandRegistry: {
7865
7865
  base64: "base64";
7866
7866
  utf8: "utf8";
7867
7867
  }>;
7868
- sha256: z$1.ZodString;
7869
- sizeBytes: z$1.ZodNumber;
7870
7868
  mimeType: z$1.ZodOptional<z$1.ZodString>;
7869
+ sizeBytes: z$1.ZodNumber;
7870
+ sha256: z$1.ZodString;
7871
7871
  modifiedAtMs: z$1.ZodOptional<z$1.ZodNumber>;
7872
7872
  notModified: z$1.ZodLiteral<true>;
7873
7873
  }, z$1.core.$strip>]>, "onlineRpc", true>;
@@ -8132,10 +8132,10 @@ declare const hostDaemonCommandRegistry: {
8132
8132
  health: z$1.ZodObject<{
8133
8133
  status: z$1.ZodEnum<{
8134
8134
  unknown: "unknown";
8135
- ready: "ready";
8136
8135
  not_installed: "not_installed";
8137
8136
  unauthenticated: "unauthenticated";
8138
8137
  expired: "expired";
8138
+ ready: "ready";
8139
8139
  unsupported_version: "unsupported_version";
8140
8140
  }>;
8141
8141
  statusMessage: z$1.ZodNullable<z$1.ZodString>;
@@ -8183,9 +8183,9 @@ declare const hostDaemonCommandRegistry: {
8183
8183
  executablePath: z$1.ZodNullable<z$1.ZodString>;
8184
8184
  installed: z$1.ZodBoolean;
8185
8185
  installSource: z$1.ZodEnum<{
8186
+ external: "external";
8186
8187
  notInstalled: "notInstalled";
8187
8188
  npmGlobal: "npmGlobal";
8188
- external: "external";
8189
8189
  }>;
8190
8190
  currentVersion: z$1.ZodNullable<z$1.ZodString>;
8191
8191
  latestVersion: z$1.ZodNullable<z$1.ZodString>;
@@ -8631,8 +8631,8 @@ declare const hostDaemonCommandRegistry: {
8631
8631
  message: z$1.ZodNullable<z$1.ZodString>;
8632
8632
  state: z$1.ZodNullable<z$1.ZodEnum<{
8633
8633
  pending: "pending";
8634
- activating: "activating";
8635
8634
  ready: "ready";
8635
+ activating: "activating";
8636
8636
  }>>;
8637
8637
  }, z$1.core.$strict>, "onlineRpc", true>;
8638
8638
  "server_move.prepare": HostDaemonCommandDescriptor<"server_move.prepare", z$1.ZodObject<{
@@ -8734,9 +8734,9 @@ declare const providerCliStatusResponseSchema: z$1.ZodRecord<z$1.ZodString, z$1.
8734
8734
  executablePath: z$1.ZodNullable<z$1.ZodString>;
8735
8735
  installed: z$1.ZodBoolean;
8736
8736
  installSource: z$1.ZodEnum<{
8737
+ external: "external";
8737
8738
  notInstalled: "notInstalled";
8738
8739
  npmGlobal: "npmGlobal";
8739
- external: "external";
8740
8740
  }>;
8741
8741
  currentVersion: z$1.ZodNullable<z$1.ZodString>;
8742
8742
  latestVersion: z$1.ZodNullable<z$1.ZodString>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@halcyonlabs/app",
3
- "version": "0.47.0-halcyon.344",
3
+ "version": "0.47.0-halcyon.346",
4
4
  "description": "bb app launcher for npx",
5
5
  "keywords": [
6
6
  "bb",
@@ -105,7 +105,7 @@
105
105
  "node": "^22.19.0 || ^24.0.0 || ^26.0.0"
106
106
  },
107
107
  "moonletRelease": {
108
- "summary": "Core change: review-slot reads only the event log's new lines (#351)"
108
+ "summary": "Core change: automatic updates don't wait on starts still waiting for an environment (#355)"
109
109
  },
110
110
  "license": "SEE LICENSE IN LICENSE"
111
111
  }
@@ -1 +1 @@
1
- 80e133034bac42499cdc417c0ef5c88708fcbef9
1
+ 11fa00903f60952aed27813063922f53bbd902c7
@@ -1156,7 +1156,7 @@ ${fh(c)}`}}),"run")}),"messages mark-read":Vf({summary:"Mark a message read (or
1156
1156
  `)}}}),"userland list":qn({summary:"What your moonlet taught itself: each artifact's state, active version, candidate and switches",options:{kind:{type:"string",description:"Only this kind"},state:{type:"string",description:`Only this state: ${AC.join(", ")}`},...Fn},async run({options:a},c){if(await s(c.threadId),a.state!==void 0&&!AC.includes(a.state))throw new In(`--state is one of ${AC.join(", ")}.`);let l=e.list({...a.kind?{kind:a.kind}:{},...a.state?{state:a.state}:{}});return a.json?{exitCode:0,stdout:JSON.stringify(l)}:{exitCode:0,stdout:l.length?l.map($C).join(`
1157
1157
  `):"Nothing yet."}}}),"userland show":qn({summary:"A version's text, stage, author and trial: the active one (refused when it's switched off or retired), else the candidate; --version for any",positionals:Ks,options:{version:{type:"string",description:"The version number"},...Fn},async run({positionals:a,options:c},l){await s(l.threadId);let u=c.version===void 0?void 0:Number(c.version);if(u!==void 0&&!Number.isInteger(u))throw new In("--version is a number.");let d=String(a.artifact);if(u===void 0&&e.list().some(f=>`${f.kind}/${f.name}`===d&&f.active!==null)){let f=e.use(d);if("state"in f)throw new In(`${f.reason}. To read it anyway: --version.`)}return ls(e.show(d,u),c.json,f=>[`${$C(f.artifact)}`,`v${f.version.n} ${f.version.stage} \xB7 by ${f.version.by} (${f.version.author}) ${OC(f.version.at)} \xB7 ${f.version.why}`,...f.evidence.map(p=>` ${p.source} ${p.outcome}: ${p.note}`),"",f.body].join(`
1158
1158
  `))}}),"userland fill":qn({summary:"A template filled with these values, escaped and checked against the page rules: the page to write (the active version, or --version for the candidate's trial)",positionals:Ks,options:{values:{type:"string",stdin:!0,description:`Each slot's value, as one line of JSON: {"place":"Lisbon"} (or --values-stdin)`},version:{type:"string",description:"The candidate in trial, to fill for its trial (default: the active version)"},...Fn},async run({positionals:a,options:c},l){let u=await s(l.threadId),d=c.version===void 0?void 0:Number(c.version);if(d!==void 0&&!(Number.isInteger(d)&&d>0))throw new In("--version is a version number.");let f;try{f=JSON.parse(c.values??"")}catch{throw new In('--values is one line of JSON naming each slot: {"place":"Lisbon"}.')}if(!f||typeof f!="object"||Array.isArray(f)||!Object.values(f).every(p=>typeof p=="string"))throw new In("--values names each slot with a text value.");return ls(e.fill(u,String(a.artifact),f,d),c.json,p=>p.text)}}),"userland history":qn({summary:"An artifact's versions and history: who, when and why",positionals:Ks,options:Fn,async run({positionals:a,options:c},l){return await s(l.threadId),ls(e.history(String(a.artifact)),c.json,u=>[$C(u.artifact),...u.versions.map(d=>`v${d.n} ${d.stage}${d.trimmed?" (trimmed)":""} \xB7 by ${d.by} ${OC(d.at)} \xB7 ${d.why}${d.reasons.length?` \xB7 ${d.reasons.join("; ")}`:""}`),"",...u.lines.map(d=>`${OC(d.at)} ${d.what}${d.n===null?"":` v${d.n}`} \xB7 ${d.by} \xB7 ${d.why}`)].join(`
1159
- `))}}),"userland draft":qn({summary:"A new version: refused with every reason, or in trial",positionals:Ks,options:{why:{type:"string",description:"Why this version (one line)"},body:{type:"string",stdin:!0,description:"The text on one line, \\n for a line break (or --body-stdin)"},...Fn},async run({positionals:a,options:c},l){let u=await s(l.threadId),d=String(a.artifact),f=e.draft(u,d,o(c.body),c.why??"");if(f.state==="refused")throw new In(f.n?`${d} v${f.n} refused: ${f.reason}`:f.reason);return{exitCode:0,stdout:c.json?JSON.stringify(f):f.activeNow?`${d} v${f.n} passed its trial and is active.`:`${d} v${f.n} is in trial (${IC(f.verdict)}). Report how it went with bb orbit userland trial ${d}.`}}}),"userland trial":qn({summary:"Report how the candidate in trial went; the kind checks it",positionals:Ks,options:{outcome:{type:"string",description:"pass or fail"},note:{type:"string",description:"What happened (one line)"},page:{type:"string",description:"The page made with it, as thr_\u2026/name (page templates)"},...Fn},async run({positionals:a,options:c},l){let u=await s(l.threadId);if(c.outcome!=="pass"&&c.outcome!=="fail")throw new In("--outcome is pass or fail.");let d;if(c.page!==void 0){let[p,m,...y]=c.page.split("/");if(!p||!m||y.length||!i5(p))throw new In("--page is thr_\u2026/name, like thr_abc/lisbon.md.");d={thread:p,name:m}}if(d&&u.role!=="owner"&&!await n(d.thread))throw new In("--page is a page from one of your moonlet's own conversations, on its stations.");let f=String(a.artifact);return ls(e.trial(u,f,{outcome:c.outcome,note:c.note??"",...d?{page:d}:{}}),c.json,p=>p.activeNow?`${p.evidence.note}. ${f} is active.`:`${p.evidence.outcome}: ${p.evidence.note}. Its trial: ${IC(p.verdict)}.`)}}),"userland activate":qn({summary:"Make a candidate whose trial passed the active version (your moonlet's main conversation, or the owner)",positionals:Ks,options:{version:{type:"string",description:"The candidate version you looked at"},...Fn},async run({positionals:a,options:c},l){let u=String(a.artifact),d=Number(c.version);if(!Number.isInteger(d)||d<1)throw new In("--version is the candidate you looked at (bb orbit userland show).");return ls(e.activate(await s(l.threadId),u,d),c.json,f=>`${u} v${f.n} is active (drafted by ${f.author}).`)}}),"userland rollback":qn({summary:"Make a version that was once active the active one again",positionals:Ks,options:{to:{type:"string",description:"The version to go back to"},why:{type:"string",description:"Why (one line)"},...Fn},async run({positionals:a,options:c},l){let u=Number(c.to);if(!Number.isInteger(u)||u<1)throw new In("--to is a version number.");let d=String(a.artifact);return ls(e.rollback(await s(l.threadId),d,u,c.why??""),c.json,f=>`${d} is back to v${u}'s text, as v${f.n}.`)}}),"userland retire":qn({summary:"Take an artifact out of use; its history is kept",positionals:Ks,options:{why:{type:"string",description:"Why (one line)"},...Fn},async run({positionals:a,options:c},l){let u=String(a.artifact);return ls(e.retire(await s(l.threadId),u,c.why??""),c.json,()=>`${u} is retired.`)}}),"userland disable":qn({summary:"Switch an artifact off: the owner's switch, or your moonlet's main conversation's pause",positionals:Ks,options:{why:{type:"string",description:"Why (one line)"},...Fn},async run({positionals:a,options:c},l){let u=String(a.artifact);return ls(e.disable(await s(l.threadId),u,c.why??""),c.json,d=>`${u} is switched off (${K0(d.holder)}'s switch).${lK(e,u)}`)}}),"userland enable":qn({summary:"Switch an artifact back on: your moonlet's main conversation clears its own switch and an automatic limit's; the owner clears any",positionals:Ks,options:{holder:{type:"string",description:`Only this switch: ${xC.join(", ")}`},...Fn},async run({positionals:a,options:c},l){if(c.holder!==void 0&&!xC.includes(c.holder))throw new In(`--holder is one of ${xC.join(", ")}.`);let u=String(a.artifact);return ls(e.enable(await s(l.threadId),u,c.holder),c.json,d=>d.stillOff.length?`Cleared ${d.cleared.map(K0).join(", ")}'s switch; ${u} stays off (${d.stillOff.map(K0).join(", ")}).`:`${u} is switched on.`)}}),"userland kind off":qn({summary:"The owner: switch a whole kind off; nothing of it loads",positionals:[{name:"kind",description:"The kind, like page-template",required:!0}],options:{why:{type:"string",description:"Why (one line)"},...Fn},async run({positionals:a,options:c},l){let u=String(a.kind);return ls(e.setKind(await s(l.threadId),u,!0,c.why??""),c.json,()=>`${u} is off.${lK(e,u)}`)}}),"userland kind on":qn({summary:"The owner: switch a whole kind back on",positionals:[{name:"kind",description:"The kind, like page-template",required:!0}],options:Fn,async run({positionals:a,options:c},l){let u=String(a.kind);return ls(e.setKind(await s(l.threadId),u,!1,""),c.json,()=>`${u} is on.`)}}),"userland purge":qn({summary:"The owner: purge a retired artifact now, freeing its name",positionals:Ks,options:Fn,async run({positionals:a,options:c},l){let u=String(a.artifact);return ls(e.purge(await s(l.threadId),u),c.json,()=>`${u} is purged.`)}})}}i(uK,"userlandCommands");import{cliCommand as Xde,PluginCliError as Mu}from"@get-bb/plugin-sdk";var Jde=500,Yde=new Set([cs,Wr]);function Zde(e){return new Set(e.filter(t=>t.machineProviderId!==null&&Yde.has(t.machineProviderId)).map(t=>t.id))}i(Zde,"stationMachineIds");function Qde(e,t){if(e.exitCode!==0||typeof e.stdout!="string")throw new Mu(`bb memory ${t} failed: ${(e.stderr??e.stdout??"").trim()||`exit ${e.exitCode}`}`);let r=JSON.parse(e.stdout);if(r===null||typeof r!="object"||r.ok!==!0)throw new Mu(`bb memory ${t} answered in an unknown shape`);return r}i(Qde,"printed");function epe(e){let t=e;if(t===null||typeof t!="object")throw new Mu("bb memory listed a record in an unknown shape");if(t.scope!=="global")return null;if(typeof t.id!="string"||typeof t.version!="number"||typeof t.name!="string"||typeof t.summary!="string"||typeof t.details!="string"||typeof t.kind!="string"||!Array.isArray(t.tags)||typeof t.importance!="number"||typeof t.pinned!="boolean"||typeof t.writeReason!="string"||t.sourceThreadId!==null&&typeof t.sourceThreadId!="string")throw new Mu("bb memory listed a global record in an unknown shape");return t}i(epe,"globalMemory");async function tpe(e){let t=await e.allMemories(),r=Zde(await e.hosts()),n={checked:0,station:[],unknownThreads:[]};for(let s of t){let o=epe(s);if(o===null)continue;n.checked+=1;let a=o.sourceThreadId;if(a===null)continue;let c=await e.threadPlace(a);if(c===null){n.unknownThreads.push({id:o.id,thread:a});continue}if(c.hostId===null||!r.has(c.hostId))continue;let l=c.projectId!==null&&await e.whollyOn(c.projectId,c.hostId)?c.projectId:null;n.station.push({memory:o,thread:a,hostId:c.hostId,target:l})}return n}i(tpe,"stationGlobals");function rpe(e){let t=`Moved from global memory by the owner. ${e.writeReason}`.slice(0,Jde);return["add","--scope=project",`--name=${e.name}`,`--summary=${e.summary}`,`--details=${e.details}`,`--kind=${e.kind}`,...e.tags.map(r=>`--tag=${r}`),`--importance=${e.importance}`,...e.pinned?["--pinned"]:[],`--reason=${t}`,"--json"]}i(rpe,"moveArgv");async function npe(e,t){if(t.target===null)throw new Mu(`${t.memory.id}: its conversation's project isn't wholly on the station, so there's no station project to move it to`);let n=Qde(await e.memory(rpe(t.memory),t.target),"add").memory?.id;if(typeof n!="string")throw new Mu(`${t.memory.id}: bb memory add answered without the new memory's id`);let s=await e.memory(["forget",t.memory.id,`--expected-version=${t.memory.version}`,"--reason=Moved into the station's own project by the owner","--json"]);return s.exitCode!==0?`${t.memory.id} \u2192 ${n} in ${t.target}, but the global copy is still there: ${(s.stderr??"").trim()||"bb memory forget failed"}. Forget it in Memory settings.`:`${t.memory.id} \u2192 ${n} in ${t.target}`}i(npe,"moveStationGlobal");function ipe(e){let t=e.target===null?"can't be moved: its conversation's project isn't wholly on the station":`moves to ${e.target}`;return`${e.memory.id} ${e.memory.name} (saved by ${e.thread}): ${t}`}i(ipe,"describe");function dK(e){return{"memory station-globals":Xde({summary:"List global memories a station's conversations saved, and move them into that station's own project (owner only)",options:{move:{type:"string",repeatable:!0,split:",",placeholder:"ID",description:"Move this memory into its station project, then forget the global copy; repeat or comma-separate"},json:{type:"boolean",description:"As JSON"}},async run({options:t},r){await e.requireUser(r.threadId);let n=await tpe(e),s=t.move??[];if(s.length===0){if(t.json)return{exitCode:0,stdout:JSON.stringify(n)};let l=[n.station.length===0?"No global memory was saved by a station's conversation.":`Saved by a station's conversation (${n.station.length}):`,...n.station.map(u=>`- ${ipe(u)}`),...n.unknownThreads.length?[`Saved by conversations bb no longer has (${n.unknownThreads.length}), so their machine is unknown:`,...n.unknownThreads.map(u=>`- ${u.id} (${u.thread})`)]:[],`Checked ${n.checked} global memories.`];return n.station.some(u=>u.target!==null)&&l.push("Move one with: bb orbit memory station-globals --move <id>"),{exitCode:0,stdout:l.join(`
1159
+ `))}}),"userland draft":qn({summary:"A new version: refused with every reason, or in trial",positionals:Ks,options:{why:{type:"string",description:"Why this version (one line)"},body:{type:"string",stdin:!0,description:"The text on one line, \\n for a line break (or --body-stdin)"},...Fn},async run({positionals:a,options:c},l){let u=await s(l.threadId),d=String(a.artifact),f=e.draft(u,d,o(c.body),c.why??"");if(f.state==="refused")throw new In(f.n?`${d} v${f.n} refused: ${f.reason}`:f.reason);return{exitCode:0,stdout:c.json?JSON.stringify(f):f.activeNow?`${d} v${f.n} passed its trial and is active.`:`${d} v${f.n} is in trial (${IC(f.verdict)}). Report how it went with bb orbit userland trial ${d}.`}}}),"userland trial":qn({summary:"Report how the candidate in trial went; the kind checks it",positionals:Ks,options:{outcome:{type:"string",description:"pass or fail"},note:{type:"string",description:"What happened (one line)"},page:{type:"string",description:"The page made with it, as thr_\u2026/name (page templates)"},...Fn},async run({positionals:a,options:c},l){let u=await s(l.threadId);if(c.outcome!=="pass"&&c.outcome!=="fail")throw new In("--outcome is pass or fail.");let d;if(c.page!==void 0){let[p,m,...y]=c.page.split("/");if(!p||!m||y.length||!i5(p))throw new In("--page is thr_\u2026/name, like thr_abc/lisbon.md.");d={thread:p,name:m}}if(d&&u.role!=="owner"&&!await n(d.thread))throw new In("--page is a page from one of your moonlet's own conversations, on its stations.");let f=String(a.artifact);return ls(e.trial(u,f,{outcome:c.outcome,note:c.note??"",...d?{page:d}:{}}),c.json,p=>p.activeNow?`${p.evidence.note}. ${f} is active.`:`${p.evidence.outcome}: ${p.evidence.note}. Its trial: ${IC(p.verdict)}.`)}}),"userland activate":qn({summary:"Make a candidate whose trial passed the active version (your moonlet's main conversation, or the owner)",positionals:Ks,options:{version:{type:"string",description:"The candidate version you looked at"},...Fn},async run({positionals:a,options:c},l){let u=String(a.artifact),d=Number(c.version);if(!Number.isInteger(d)||d<1)throw new In("--version is the candidate you looked at (bb orbit userland show).");return ls(e.activate(await s(l.threadId),u,d),c.json,f=>`${u} v${f.n} is active (drafted by ${f.author}).`)}}),"userland rollback":qn({summary:"Make a version that was once active the active one again",positionals:Ks,options:{to:{type:"string",description:"The version to go back to"},why:{type:"string",description:"Why (one line)"},...Fn},async run({positionals:a,options:c},l){let u=Number(c.to);if(!Number.isInteger(u)||u<1)throw new In("--to is a version number.");let d=String(a.artifact);return ls(e.rollback(await s(l.threadId),d,u,c.why??""),c.json,f=>`${d} is back to v${u}'s text, as v${f.n}.`)}}),"userland retire":qn({summary:"Take an artifact out of use; its history is kept",positionals:Ks,options:{why:{type:"string",description:"Why (one line)"},...Fn},async run({positionals:a,options:c},l){let u=String(a.artifact);return ls(e.retire(await s(l.threadId),u,c.why??""),c.json,()=>`${u} is retired.`)}}),"userland disable":qn({summary:"Switch an artifact off: the owner's switch, or your moonlet's main conversation's pause",positionals:Ks,options:{why:{type:"string",description:"Why (one line)"},...Fn},async run({positionals:a,options:c},l){let u=String(a.artifact);return ls(e.disable(await s(l.threadId),u,c.why??""),c.json,d=>`${u} is switched off (${K0(d.holder)}'s switch).${lK(e,u)}`)}}),"userland enable":qn({summary:"Switch an artifact back on: your moonlet's main conversation clears its own switch and an automatic limit's; the owner clears any",positionals:Ks,options:{holder:{type:"string",description:`Only this switch: ${xC.join(", ")}`},...Fn},async run({positionals:a,options:c},l){if(c.holder!==void 0&&!xC.includes(c.holder))throw new In(`--holder is one of ${xC.join(", ")}.`);let u=String(a.artifact);return ls(e.enable(await s(l.threadId),u,c.holder),c.json,d=>d.stillOff.length?`Cleared ${d.cleared.map(K0).join(", ")}'s switch; ${u} stays off (${d.stillOff.map(K0).join(", ")}).`:`${u} is switched on.`)}}),"userland kind off":qn({summary:"The owner: switch a whole kind off; nothing of it loads",positionals:[{name:"kind",description:"The kind, like page-template",required:!0}],options:{why:{type:"string",description:"Why (one line)"},...Fn},async run({positionals:a,options:c},l){let u=String(a.kind);return ls(e.setKind(await s(l.threadId),u,!0,c.why??""),c.json,()=>`${u} is off.${lK(e,u)}`)}}),"userland kind on":qn({summary:"The owner: switch a whole kind back on",positionals:[{name:"kind",description:"The kind, like page-template",required:!0}],options:Fn,async run({positionals:a,options:c},l){let u=String(a.kind);return ls(e.setKind(await s(l.threadId),u,!1,""),c.json,()=>`${u} is on.`)}}),"userland purge":qn({summary:"The owner: purge a retired artifact now, freeing its name",positionals:Ks,options:Fn,async run({positionals:a,options:c},l){let u=String(a.artifact);return ls(e.purge(await s(l.threadId),u),c.json,()=>`${u} is purged.`)}})}}i(uK,"userlandCommands");import{cliCommand as Xde,PluginCliError as Mu}from"@get-bb/plugin-sdk";var Jde=500,Yde=new Set([cs,Wr]);function Zde(e){return new Set(e.filter(t=>t.machineProviderId!==null&&Yde.has(t.machineProviderId)).map(t=>t.id))}i(Zde,"stationMachineIds");function Qde(e,t){if(e.exitCode!==0||typeof e.stdout!="string")throw new Mu(`Memory ${t} failed: ${(e.stderr??e.stdout??"").trim()||`exit ${e.exitCode}`}`);let r=JSON.parse(e.stdout);if(r===null||typeof r!="object"||r.ok!==!0)throw new Mu(`Memory ${t} answered in an unknown shape`);return r}i(Qde,"printed");function epe(e){let t=e;if(t===null||typeof t!="object")throw new Mu("Memory listed a record in an unknown shape");if(t.scope!=="global")return null;if(typeof t.id!="string"||typeof t.version!="number"||typeof t.name!="string"||typeof t.summary!="string"||typeof t.details!="string"||typeof t.kind!="string"||!Array.isArray(t.tags)||typeof t.importance!="number"||typeof t.pinned!="boolean"||typeof t.writeReason!="string"||t.sourceThreadId!==null&&typeof t.sourceThreadId!="string")throw new Mu("Memory listed a global record in an unknown shape");return t}i(epe,"globalMemory");async function tpe(e){let t=await e.allMemories(),r=Zde(await e.hosts()),n={checked:0,station:[],unknownThreads:[]};for(let s of t){let o=epe(s);if(o===null)continue;n.checked+=1;let a=o.sourceThreadId;if(a===null)continue;let c=await e.threadPlace(a);if(c===null){n.unknownThreads.push({id:o.id,thread:a});continue}if(c.hostId===null||!r.has(c.hostId))continue;let l=c.projectId!==null&&await e.whollyOn(c.projectId,c.hostId)?c.projectId:null;n.station.push({memory:o,thread:a,hostId:c.hostId,target:l})}return n}i(tpe,"stationGlobals");function rpe(e){let t=`Moved from global memory by the owner. ${e.writeReason}`.slice(0,Jde);return["add","--scope=project",`--name=${e.name}`,`--summary=${e.summary}`,`--details=${e.details}`,`--kind=${e.kind}`,...e.tags.map(r=>`--tag=${r}`),`--importance=${e.importance}`,...e.pinned?["--pinned"]:[],`--reason=${t}`,"--json"]}i(rpe,"moveArgv");async function npe(e,t){if(t.target===null)throw new Mu(`${t.memory.id}: its conversation's project isn't wholly on the station, so there's no station project to move it to`);let n=Qde(await e.memory(rpe(t.memory),t.target),"add").memory?.id;if(typeof n!="string")throw new Mu(`${t.memory.id}: memory add answered without the new memory's id`);let s=await e.memory(["forget",t.memory.id,`--expected-version=${t.memory.version}`,"--reason=Moved into the station's own project by the owner","--json"]);return s.exitCode!==0?`${t.memory.id} \u2192 ${n} in ${t.target}, but the global copy is still there: ${(s.stderr??"").trim()||"forgetting it failed"}. Forget it in Memory settings.`:`${t.memory.id} \u2192 ${n} in ${t.target}`}i(npe,"moveStationGlobal");function ipe(e){let t=e.target===null?"can't be moved: its conversation's project isn't wholly on the station":`moves to ${e.target}`;return`${e.memory.id} ${e.memory.name} (saved by ${e.thread}): ${t}`}i(ipe,"describe");function dK(e){return{"memory station-globals":Xde({summary:"List global memories a station's conversations saved, and move them into that station's own project (owner only)",options:{move:{type:"string",repeatable:!0,split:",",placeholder:"ID",description:"Move this memory into its station project, then forget the global copy; repeat or comma-separate"},json:{type:"boolean",description:"As JSON"}},async run({options:t},r){await e.requireUser(r.threadId);let n=await tpe(e),s=t.move??[];if(s.length===0){if(t.json)return{exitCode:0,stdout:JSON.stringify(n)};let l=[n.station.length===0?"No global memory was saved by a station's conversation.":`Saved by a station's conversation (${n.station.length}):`,...n.station.map(u=>`- ${ipe(u)}`),...n.unknownThreads.length?[`Saved by conversations that no longer exist (${n.unknownThreads.length}), so their machine is unknown:`,...n.unknownThreads.map(u=>`- ${u.id} (${u.thread})`)]:[],`Checked ${n.checked} global memories.`];return n.station.some(u=>u.target!==null)&&l.push("Move one with: bb orbit memory station-globals --move <id>"),{exitCode:0,stdout:l.join(`
1160
1160
  `)}}let o=new Map(n.station.map(l=>[l.memory.id,l])),a=s.filter(l=>!o.has(l));if(a.length)throw new Mu(`Not a global memory saved by a station's conversation: ${a.join(", ")}`);let c=[];for(let l of s)c.push(await npe(e,o.get(l)));return{exitCode:0,stdout:t.json?JSON.stringify({moved:c}):c.join(`
1161
1161
  `)}}})}}i(dK,"memoryCommands");var Kf=4e3;function hK(e){let{bb:t,wakeCounts:r,work:n,drains:s,settings:o,stations:a,scheduler:c,upkeep:l,notices:u,orbitConfig:d,ownerChanges:f,gate:p,relay:m,messages:y,threads:g,stationHosts:h,clock:w,zone:v,backThread:T,readAvatar:b,requireUser:$,requireUserOrOrbit:N,requireOrbitThread:L,requireOrbitConversation:k,orbitStationName:x,orbitSection:S,orbitThread:A,routeTargets:E,ensureProjectAt:C,syncStations:W}=e;async function M(O){if(O&&await L(O).catch(()=>null))return{kind:"orbit"};try{await $(O)}catch{throw new Pe("Only the Moonlet conversation, or the owner on their own machine, follows change requests.")}return{kind:"owner"}}i(M,"changesCaller");let K=i(()=>v().catch(()=>null),"settingsZone"),X=i((O,j,B)=>u.notify([{type:O,text:j}],void 0,B),"notify"),G=i((O,j)=>j===O?void 0:j,"askedFrom"),q=i(async O=>{let j=await t.sdk.projects.list(),B=[];for(let z of await h()){let ie=await pr(z.name).catch(()=>null),re=ie&&Xr(ie),P=re?j.find(Q=>Q.sources.some(J=>J.hostId===z.id&&J.path===re)):void 0;B.push({station:z.name,hostId:z.id,project:P?.id??null})}return _9(B,(await zi(t))?.hostId??null,O)},"orbitProjectFor"),ne=P0(t),U=i(async O=>{let j=await Cf(t,o);if(!j||O!==j)throw new Pe("Only the current background conversation starts background work.");return j},"requireBackground"),F=i(async O=>{O&&O===await Cf(t,o)||await N(O)},"requireUpkeep");function R(O,j,B){return{origin:"plugin",originPluginId:t.pluginId,projectId:j,environment:{type:"host",hostId:O.id,workspace:{type:"unmanaged",path:O.folder}},startedOnBehalfOf:null,originKind:null,...B}}i(R,"stationRequest");let V=i(O=>[{type:"text",text:O,mentions:[]}],"text"),_=i(O=>aK(n.items,{environments:i((j,B,z)=>t.sdk.environments.list({environmentProviderId:Br,status:B,limit:100,offset:z,...j?{hostId:j}:{}}),"environments"),threads:i((j,B)=>t.sdk.threads.list({environmentId:j,archived:!1,includeHidden:!0,limit:100,offset:B}),"threads")},O),"untracked");async function I(O,j,B,z,ie){if(z&&ie)throw new Pe("Name a task or a system work record, not both");!z&&!ie&&/^(review|merge-reviewed):/.test(O)&&(ie=(await Ws(t).change({action:"create",key:O,kind:"work",title:j,description:B,labels:["event-service"],parent:null},"system:review-start")).id);let re=await T();if(!re)throw new Pe("The background work station is unavailable.");let P=await fetch(`${t.server.loopbackBaseUrl}/api/v1/threads/${re}?include=environment`);if(!P.ok)throw new Pe("The background work station is unavailable.");let Q=await P.json(),J=(await E()).find($e=>$e.id===Q.environment?.hostId&&!$e.own);if(!J)throw new Pe("The background work station is unavailable.");let ae=d.prefs(),ce=fT(ae.models.station_provider,!1,J.codex);if(ce)throw new Pe(ce);let ve=await C(J.id,J.folder,aC(J),!J.own);return n.start({key:O,class:"background",loop:z,record:ie,station:{hostId:J.id,projectIds:new Set([ve])},body:R(J,ve,{title:j,input:V(B),...Qs(ae.models.station_provider,!0),model:ae.models.station,parentThreadId:re,visibility:"hidden"})})}i(I,"spawnBackground");let D=uG(t,{start:I});t.cli.register(spe({name:"orbit",summary:"Moonlet: station's credentials and status",commands:{...B9(t),...D9(t,N),...cK(n,async O=>{if(!O)return await $(O),null;if(O===await A()||O===await T())return null;let j=await t.sdk.threads.get({threadId:O}),B=j.environmentId?await t.sdk.environments.get({environmentId:j.environmentId}):null;if(B&&(await h()).some(z=>z.id===B.hostId))return B.hostId;throw new Pe("Work commands are called by the owner or an assistant on its own station.")},s,$,async O=>(await h()).find(j=>j.name===O)?.id??null,_,async(O,j)=>{let B=await e.orbitConversationOf(O);if(!B)return!0;if(O!==B.orbit)return!1;let z=new Set([B.orbit]),ie=await T();ie&&z.add(ie);let re=new Set((await E()).filter(P=>!P.own).map(P=>P.id));return oK(j,z,re,async P=>(await t.sdk.threads.get({threadId:P}).catch(()=>null))?.parentThreadId??null)}),...J9(y),...dK({requireUser:$,async allMemories(){let O=await fetch(`${t.server.loopbackBaseUrl}/api/v1/plugins/memory/rpc/listMemories`,{method:"POST",headers:{"content-type":"application/json"},body:"null"});if(!O.ok)throw new Pe(`bb memory isn't answering (HTTP ${O.status}); is the Memory plugin installed and running?`);let j=await O.json();if(j.ok!==!0||!Array.isArray(j.result?.memories))throw new Pe("bb memory listed its memories in an unknown shape");return j.result.memories},async memory(O,j){let B=await fetch(`${t.server.loopbackBaseUrl}/api/v1/plugins/memory/cli`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(j===void 0?{argv:O}:{argv:O,projectId:j})});if(!B.ok)throw new Pe(`bb memory isn't answering (HTTP ${B.status}); is the Memory plugin installed and running?`);return await B.json()},async threadPlace(O){let j=await fetch(`${t.server.loopbackBaseUrl}/api/v1/threads/${O}?include=environment`);if(j.status===404)return null;if(!j.ok)throw new Pe(`bb couldn't say where ${O} ran (HTTP ${j.status})`);let B=await j.json(),z=(B.thread??B).projectId,ie=B.environment?.hostId;return{hostId:typeof ie=="string"?ie:null,projectId:typeof z=="string"?z:null}},hosts:i(async()=>(await t.sdk.hosts.list()).map(O=>({id:O.id,machineProviderId:O.machineProviderId??null})),"hosts"),async whollyOn(O,j){let B=(await t.sdk.projects.list()).find(z=>z.id===O);return!!B&&B.sources.length>0&&B.sources.every(z=>z.hostId===j)}}),...K9({welcome:e.welcome,requireUser:$,changed:i(()=>t.realtime.publish("config",{changed:!0}),"changed")}),...uK({store:e.userland,orbitConversationOf:e.orbitConversationOf,requireUser:$,stationThread:i(async O=>{let j=await fetch(`${t.server.loopbackBaseUrl}/api/v1/threads/${O}?include=environment`),B=j.ok?(await j.json()).environment?.hostId:void 0;return!!B&&(await h()).some(z=>z.id===B)},"stationThread")}),"secret set":ct({summary:"Store station's credentials (values are read from stdin, never arguments)",options:{tailscale:{type:"string",stdin:!0,description:"Tailscale OAuth client secret (--tailscale-stdin)"},claude:{type:"string",stdin:!0,description:"Claude Code token from `claude setup-token` (--claude-stdin)"},onepassword:{type:"string",stdin:!0,description:"1Password service account token (--onepassword-stdin)"},calendar:{type:"string",stdin:!0,description:"The calendar's private link, handed to every station (--calendar-stdin)"}},async run({options:O},j){await $(j.threadId);let B={};if(O.tailscale!==void 0){let P=O.tailscale.trim();if(!PB(P))throw new Pe("That isn't a Tailscale credential (tskey-\u2026).");B.tailscaleSecret=P}if(O.claude!==void 0){let P=O.claude.trim();if(!/^sk-ant-[A-Za-z0-9_-]+$/.test(P))throw new Pe("That isn't a Claude Code token (sk-ant-\u2026).");B.claudeToken=P}if(O.onepassword!==void 0){let P=O.onepassword.trim();if(!/^ops_[A-Za-z0-9_-]+$/.test(P))throw new Pe("That isn't a 1Password service account token (ops_\u2026).");B.onePasswordToken=P}if(O.calendar!==void 0)try{B.calendarLink=O0("calendar",O.calendar)}catch(P){throw new Pe(SC(P)??String(P))}let z=Object.keys(B);if(z.length===0)throw new Pe("Pass --tailscale-stdin, --claude-stdin, --onepassword-stdin or --calendar-stdin.");await o.experimental_set(B);let ie=`Stored ${z.join(" and ")}.${B.onePasswordToken?" Stations fetch the new 1Password token on their next use of it.":""}`;if(B.calendarLink===void 0)return{exitCode:0,stdout:ie};let re=await EC(e.welcome);return{exitCode:re.exitCode,stdout:`${ie}
1162
1162
  ${re.stdout}`}}}),"welcome show":ct({summary:"Show first run's saved answers: which steps are saved, and whether it's finished (--json: the saved answers)",options:{json:{type:"boolean",description:"Print the saved answers as JSON"}},async run({options:O},j){return await $(j.threadId),eK(e.welcome,O.json===!0)}}),"welcome save":ct({summary:`Save one step's answers, as first run and Settings do (steps: ${hi.join(", ")})`,positionals:[{name:"step",description:"The step",required:!0}],options:{values:{type:"string",stdin:!0,description:`The step's answers as one line of JSON, e.g. {"you":{"name":"Sam"}} (or --values-stdin)`}},async run({positionals:O,options:j},B){return await $(B.threadId),RC(()=>tK(e.welcome,String(O.step),j.values))}}),"welcome places":ct({summary:"The places a city's name could mean, best first, as JSON (for the you step's place)",positionals:[{name:"query",description:"The city",required:!0}],async run({positionals:O},j){return await $(j.threadId),RC(()=>rK(e.welcome,String(O.query)))}}),"welcome handover":ct({summary:"Hand the contacts and the calendar to every station again, and say how each went",async run(O,j){return await $(j.threadId),EC(e.welcome)}}),"welcome finish":ct({summary:"Finish first run: tell your moonlet's conversation what was saved, once (now, or when its conversation starts)",async run(O,j){await $(j.threadId);let B=await RC(()=>nK(e.welcome));return t.realtime.publish("config",{changed:!0}),B}}),"welcome reopen":ct({summary:"Set up again: open first run in Moonlet again, keeping its answers",async run(O,j){await $(j.threadId);let B=await iK(e.welcome);return t.realtime.publish("config",{changed:!0}),B}}),"station sync":ct({summary:"Give every connected station its project and its broker's credentials",async run(){let O=await W();return{exitCode:O.some(j=>j.includes("failed"))?1:0,stdout:O.join(`
@@ -43136,6 +43136,20 @@ function listRunningThreads(db) {
43136
43136
  )
43137
43137
  ).orderBy(asc(threads.id)).all().map((row) => ({ ...row, hostId: row.hostId ?? null }));
43138
43138
  }
43139
+ function countThreadsAnUpdateWouldInterrupt(db) {
43140
+ return db.select({ value: count() }).from(threads).where(
43141
+ and(
43142
+ liveThreads(),
43143
+ or(
43144
+ eq(threads.status, "active"),
43145
+ and(
43146
+ eq(threads.status, "starting"),
43147
+ isNotNull(threads.environmentId)
43148
+ )
43149
+ )
43150
+ )
43151
+ ).get()?.value ?? 0;
43152
+ }
43139
43153
  function listArchivedThreadsPendingTeardown(db) {
43140
43154
  const selection = {
43141
43155
  archivedAt: threads.archivedAt,
@@ -238475,7 +238489,7 @@ async function runServer(serverConfig) {
238475
238489
  appSurface: serverConfig.BB_APP_SURFACE,
238476
238490
  appVersion,
238477
238491
  config: runtimeConfig,
238478
- countRunningThreads: () => listRunningThreads(db).length,
238492
+ countRunningThreads: () => countThreadsAnUpdateWouldInterrupt(db),
238479
238493
  launcher: appUpdateMode === null ? null : createLauncherChannel(process),
238480
238494
  logger: logger2,
238481
238495
  mode: appUpdateMode,
@@ -238485,7 +238499,7 @@ async function runServer(serverConfig) {
238485
238499
  channel: desktopUpdateChannel,
238486
238500
  currentVersion: runtimeConfig.appVersion,
238487
238501
  dataDir: serverConfig.BB_DATA_DIR,
238488
- countRunningThreads: () => listRunningThreads(db).length,
238502
+ countRunningThreads: () => countThreadsAnUpdateWouldInterrupt(db),
238489
238503
  notifyChanged: () => hub.notifySystem(["app-update-changed"]),
238490
238504
  logger: logger2
238491
238505
  });