@ai-ecoverse/slicc-agent 3.5.0 → 3.7.0
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/README.md +21 -4
- package/bin/agent +76 -0
- package/dist/agent.js +7 -14
- package/dist/agent.js.map +2 -2
- package/dist/agents.js +133 -0
- package/dist/agents.js.map +7 -0
- package/dist/client.js +3 -1
- package/dist/client.js.map +2 -2
- package/dist/cone.js +2 -16
- package/dist/cone.js.map +2 -2
- package/dist/control.js +68 -10
- package/dist/control.js.map +2 -2
- package/dist/host.js +11 -2
- package/dist/host.js.map +2 -2
- package/dist/kernel/env.js +49 -5
- package/dist/kernel/env.js.map +2 -2
- package/dist/kernel/exec.js +2 -1
- package/dist/kernel/exec.js.map +2 -2
- package/dist/kernel/groups.js +31 -0
- package/dist/kernel/groups.js.map +7 -0
- package/dist/kernel/watch.js +86 -0
- package/dist/kernel/watch.js.map +2 -2
- package/dist/licks/index.js +16 -1
- package/dist/licks/index.js.map +2 -2
- package/dist/licks/lick.js.map +1 -1
- package/dist/licks/licks.js +12 -1
- package/dist/licks/licks.js.map +2 -2
- package/dist/main.js +38 -9
- package/dist/main.js.map +2 -2
- package/dist/roles/frontmatter.js +59 -0
- package/dist/roles/frontmatter.js.map +7 -0
- package/dist/roles/roles.js +204 -0
- package/dist/roles/roles.js.map +7 -0
- package/dist/scoops/cli.js +391 -0
- package/dist/scoops/cli.js.map +7 -0
- package/dist/scoops/extension.js +301 -0
- package/dist/scoops/extension.js.map +7 -0
- package/dist/scoops/identity.js +33 -0
- package/dist/scoops/identity.js.map +7 -0
- package/dist/scoops/index.js +120 -0
- package/dist/scoops/index.js.map +7 -0
- package/dist/scoops/requests.js +136 -0
- package/dist/scoops/requests.js.map +7 -0
- package/dist/scoops/service.js +409 -0
- package/dist/scoops/service.js.map +7 -0
- package/dist/scoops/sync.js +252 -0
- package/dist/scoops/sync.js.map +7 -0
- package/dist/scoops/visible.js +15 -0
- package/dist/scoops/visible.js.map +7 -0
- package/dist/services.js.map +2 -2
- package/dist/spectrum/agent-port.js +137 -54
- package/dist/spectrum/agent-port.js.map +2 -2
- package/dist/spectrum/messages.js +19 -10
- package/dist/spectrum/messages.js.map +2 -2
- package/dist/types/agent.d.ts +3 -1
- package/dist/types/agents.d.ts +54 -0
- package/dist/types/client.d.ts +3 -1
- package/dist/types/cone.d.ts +1 -3
- package/dist/types/control.d.ts +7 -1
- package/dist/types/host.d.ts +2 -1
- package/dist/types/kernel/client.d.ts +13 -0
- package/dist/types/kernel/env.d.ts +13 -2
- package/dist/types/kernel/exec.d.ts +2 -2
- package/dist/types/kernel/groups.d.ts +7 -0
- package/dist/types/kernel/watch.d.ts +12 -1
- package/dist/types/licks/index.d.ts +5 -0
- package/dist/types/licks/lick.d.ts +1 -1
- package/dist/types/licks/licks.d.ts +2 -1
- package/dist/types/main.d.ts +3 -0
- package/dist/types/roles/frontmatter.d.ts +7 -0
- package/dist/types/roles/roles.d.ts +48 -0
- package/dist/types/scoops/cli.d.ts +66 -0
- package/dist/types/scoops/extension.d.ts +63 -0
- package/dist/types/scoops/identity.d.ts +6 -0
- package/dist/types/scoops/index.d.ts +37 -0
- package/dist/types/scoops/requests.d.ts +32 -0
- package/dist/types/scoops/service.d.ts +116 -0
- package/dist/types/scoops/sync.d.ts +22 -0
- package/dist/types/scoops/visible.d.ts +4 -0
- package/dist/types/services.d.ts +30 -0
- package/dist/types/spectrum/agent-port.d.ts +13 -11
- package/dist/types/views.d.ts +12 -0
- package/dist/views.js +89 -0
- package/dist/views.js.map +7 -0
- package/package.json +6 -4
- package/packages/vfs-root/agents/delegate.md +8 -0
- package/packages/vfs-root/agents/oracle.md +25 -0
- package/packages/vfs-root/agents/reviewer.md +23 -0
- package/packages/vfs-root/agents/scout.md +30 -0
- package/packages/vfs-root/agents/worker.md +23 -0
- package/packages/vfs-root/skills/agent/SKILL.md +72 -0
package/dist/control.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/control.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n type AgentChange,\n AssistantEntry,\n type Conversation,\n ConversationBusy,\n type ConversationView,\n type EntryId,\n type EntryRecord,\n type Harness,\n type SubmissionId,\n} from '@earendil-works/pi-durable';\nimport { answerText } from './agent.ts';\nimport type { Cones } from './cone.ts';\nimport type { Deliveries } from './deliveries.ts';\nimport type { LickSources, Licks } from './licks/index.ts';\nimport { LICK_STATE_KIND } from './licks/state.ts';\nimport type { AgentControl, OperationError, SendResponse } from './services.ts';\n\nexport interface HostLicks {\n licks: Licks;\n sources: LickSources;\n}\n\nexport function submissionId(value: string): SubmissionId | undefined {\n const id = Number(value);\n return /^[1-9][0-9]*$/.test(value) && Number.isSafeInteger(id) ? (id as SubmissionId) : undefined;\n}\n\nfunction operationError(error: unknown): OperationError {\n if (error instanceof ConversationBusy) return { code: 'busy', message: error.message };\n return { code: 'failed', message: error instanceof Error ? error.message : String(error) };\n}\n\nasync function accepted(operation: () => Promise<{ id: number } | number>): Promise<SendResponse> {\n try {\n const result = await operation();\n const id = typeof result === 'number' ? result : result.id;\n return { accepted: true, submissionId: String(id), error: null };\n } catch (error) {\n return { accepted: false, submissionId: null, error: operationError(error) };\n }\n}\n\nfunction cones(target: Conversation | Cones): Cones {\n if ('cone' in target) return target;\n return {\n cone: () => target,\n switchCone: async () => {\n throw new Error('this conversation has no cone to rewind');\n },\n };\n}\n\ninterface Turn {\n text: string;\n at: EntryId | null;\n}\n\nfunction lastTurn(view: ConversationView, messageId: string | null): Turn | null {\n const { entries } = view;\n const target = entries.findIndex((entry) => `e${entry.id}` === messageId);\n if (target < 0) return null;\n const turn = entries.slice(0, target).findLastIndex((entry) => entry.kind === 'pi.user');\n if (turn < 0) return null;\n const user = entries[turn] as EntryRecord;\n return { text: answerText(user.model?.[0]?.content), at: entries[turn - 1]?.id ?? null };\n}\n\nfunction busy(view: ConversationView): boolean {\n const live = view.docs['pi.live'] as { run?: unknown } | undefined;\n const inbox = view.docs['pi.inbox'] as { items?: unknown[] } | undefined;\n return Boolean(live?.run) || Boolean(inbox?.items?.length);\n}\n\nexport function createAgentControl(\n harness: Harness,\n target: Conversation | Cones,\n deliveries?: Deliveries,\n licks?: HostLicks\n): AgentControl {\n const cone = cones(target);\n const current = () => cone.cone();\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const run = lock.then(operation, operation);\n lock = run.catch(() => undefined);\n return run;\n };\n return {\n send(request, context) {\n return serial(async () => {\n const response = await accepted(() =>\n current().submit(\n {\n type: 'input',\n content: request.text,\n whenBusy: request.whenBusy,\n ...(request.requestId ? { requestId: request.requestId } : {}),\n },\n context\n )\n );\n const id = response.submissionId === null ? undefined : submissionId(response.submissionId);\n if (id !== undefined) deliveries?.expect(id, request.whenBusy);\n return response;\n });\n },\n rewind(messageId, context) {\n return serial(async () => {\n const conversation = current();\n const attached = await conversation.viewState(context);\n const view = attached.value;\n attached.dispose();\n const pending = (await harness.inspect(context)).submissions.some(\n (submission) => submission.conversationId === conversation.id\n );\n if (pending || busy(view)) return { done: false, text: null, reason: 'busy' };\n const turn = lastTurn(view, messageId);\n if (!turn) return { done: false, text: null, reason: 'no-turn' };\n const agent = view.docs['pi.agent'] as AgentChange | undefined;\n const ownership = { kind: 'ownerless' } as const;\n const fork =\n turn.at === null\n ? await harness.createConversation(\n {\n ownership,\n agent: {\n model: agent?.model ?? null,\n thinkingLevel: agent?.thinkingLevel ?? null,\n },\n },\n context\n )\n : await conversation.fork(turn.at, { ownership }, context);\n await fork.submit(\n { type: 'write', entry: { kind: 'slicc.rewound', data: { turns: 1 } } },\n context\n );\n await cone.switchCone(fork, context);\n return { done: true, text: turn.text, reason: null };\n });\n },\n\n async wait(id, context) {\n const parsed = submissionId(id);\n const submission =\n parsed === undefined ? undefined : await harness.submission(parsed, context);\n if (!submission) throw new Error(`unknown submission ${id}`);\n const settled = await submission.wait(context);\n const { answer, reason } = settled as { answer?: EntryId; reason?: string };\n if (answer === undefined) return { status: 'unanswered', text: null, reason: String(reason) };\n const entry = await harness.commit((tx) => tx.entry(AssistantEntry, answer), context);\n return { status: 'done', text: answerText(entry?.model?.[0]?.content), reason: null };\n },\n async withdraw(id, context) {\n const parsed = submissionId(id);\n if (parsed === undefined) return { outcome: 'not_found' };\n const result = await harness.abortSubmission(parsed, context, current().id);\n if (result === 'aborted') return { outcome: 'withdrawn' };\n return { outcome: result === 'not_found' ? 'not_found' : 'already_placed' };\n },\n abort: (context) => current().abort(context),\n compact: (instructions, context) =>\n accepted(() => current().compact(instructions ?? undefined, context)),\n reset: (handoff, context) => current().reset(handoff ?? undefined, context),\n resolveLick(lickId, state, context) {\n return serial(async () => {\n if (!licks) return { done: false, text: null, error: 'this agent has no licks' };\n const conversation = current();\n const action = state === 'confirmed' ? 'confirm' : 'dismiss';\n try {\n const text = await licks.licks.decide(conversation, lickId, action, undefined, context);\n await conversation.submit(\n {\n type: 'write',\n entry: {\n kind: LICK_STATE_KIND,\n data: { lick: lickId, state, by: 'user' },\n model: [\n {\n role: 'user',\n content: `The user ${state} lick ${lickId}: ${text}`,\n timestamp: Date.now(),\n },\n ],\n },\n },\n context\n );\n return { done: true, text, error: null };\n } catch (error) {\n return { done: false, text: null, error: (error as Error).message };\n }\n });\n },\n async webhook(name, delivery, context) {\n const delivered = licks\n ? await licks.sources.webhook(\n name,\n {\n ...(delivery.id ? { id: delivery.id } : {}),\n headers: delivery.headers,\n body: delivery.body,\n },\n context\n )\n : false;\n return { delivered };\n },\n configure: (change, context) =>\n current().configure(\n {\n ...(change.model ? { model: change.model } : {}),\n ...(change.thinkingLevel ? { thinkingLevel: change.thinkingLevel } : {}),\n },\n context\n ),\n };\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type AgentChange,\n AssistantEntry,\n type Conversation,\n ConversationBusy,\n type ConversationView,\n type EntryId,\n type EntryRecord,\n type Harness,\n type SubmissionId,\n} from '@earendil-works/pi-durable';\nimport { answerText } from './agent.ts';\nimport type { Agents } from './agents.ts';\nimport type { Cones } from './cone.ts';\nimport type { Deliveries } from './deliveries.ts';\nimport type { LickSources, Licks } from './licks/index.ts';\nimport { LICK_STATE_KIND } from './licks/state.ts';\nimport type { Scoops, ScoopsRuntime } from './scoops/index.ts';\nimport type { AgentControl, Created, OperationError, SendResponse } from './services.ts';\n\nexport interface HostLicks {\n licks: Licks;\n sources: LickSources;\n}\n\nexport interface HostScoops {\n scoops: Scoops;\n runtime: ScoopsRuntime;\n}\n\ntype WithAgents = { agents?: Agents };\n\nexport function readable(out: string): string {\n const text = out.replace(/^(?:sub)?agent: /, '').trim();\n const sentence = text.charAt(0).toUpperCase() + text.slice(1);\n return /[.!?]$/.test(sentence) ? sentence : `${sentence}.`;\n}\n\nfunction created(run: () => Promise<string>): Promise<Created> {\n return run().then(\n (id) => ({ id, error: null }),\n (error: Error) => ({ id: null, error: error.message })\n );\n}\n\nexport function submissionId(value: string): SubmissionId | undefined {\n const id = Number(value);\n return /^[1-9][0-9]*$/.test(value) && Number.isSafeInteger(id) ? (id as SubmissionId) : undefined;\n}\n\nfunction operationError(error: unknown): OperationError {\n if (error instanceof ConversationBusy) return { code: 'busy', message: error.message };\n return { code: 'failed', message: error instanceof Error ? error.message : String(error) };\n}\n\nasync function accepted(operation: () => Promise<{ id: number } | number>): Promise<SendResponse> {\n try {\n const result = await operation();\n const id = typeof result === 'number' ? result : result.id;\n return { accepted: true, submissionId: String(id), error: null };\n } catch (error) {\n return { accepted: false, submissionId: null, error: operationError(error) };\n }\n}\n\nfunction cones(target: Conversation | Cones): Cones {\n if ('cone' in target) return target;\n return {\n cone: () => target,\n switchCone: async () => {\n throw new Error('this conversation has no cone to rewind');\n },\n };\n}\n\ninterface Turn {\n text: string;\n at: EntryId | null;\n}\n\nfunction lastTurn(view: ConversationView, messageId: string | null): Turn | null {\n const { entries } = view;\n const target = entries.findIndex((entry) => `e${entry.id}` === messageId);\n if (target < 0) return null;\n const turn = entries.slice(0, target).findLastIndex((entry) => entry.kind === 'pi.user');\n if (turn < 0) return null;\n const user = entries[turn] as EntryRecord;\n return { text: answerText(user.model?.[0]?.content), at: entries[turn - 1]?.id ?? null };\n}\n\nfunction busy(view: ConversationView): boolean {\n const live = view.docs['pi.live'] as { run?: unknown } | undefined;\n const inbox = view.docs['pi.inbox'] as { items?: unknown[] } | undefined;\n return Boolean(live?.run) || Boolean(inbox?.items?.length);\n}\n\nexport function createAgentControl(\n harness: Harness,\n target: Conversation | Cones,\n deliveries?: Deliveries,\n licks?: HostLicks,\n scoops?: HostScoops\n): AgentControl {\n const cone = cones(target);\n const current = () => cone.cone();\n const agents = (target as WithAgents).agents;\n const conversationFor = async (agentId: string | null | undefined, context: Context) => {\n if (!agentId || !agents) return current();\n const found = await agents.conversation(agentId, context);\n if (!found) throw new Error(`There is no agent ${agentId}.`);\n return found;\n };\n const withdraw = async (conversation: Conversation, id: string, context: Context) => {\n const parsed = submissionId(id);\n if (parsed === undefined) return { outcome: 'not_found' as const };\n const result = await harness.abortSubmission(parsed, context, conversation.id);\n if (result === 'aborted') return { outcome: 'withdrawn' as const };\n return {\n outcome: result === 'not_found' ? ('not_found' as const) : ('already_placed' as const),\n };\n };\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const run = lock.then(operation, operation);\n lock = run.catch(() => undefined);\n return run;\n };\n return {\n send(request, context) {\n return serial(async () => {\n const response = await accepted(async () =>\n (await conversationFor(request.agentId, context)).submit(\n {\n type: 'input',\n content: request.text,\n whenBusy: request.whenBusy,\n ...(request.requestId ? { requestId: request.requestId } : {}),\n },\n context\n )\n );\n const id = response.submissionId === null ? undefined : submissionId(response.submissionId);\n if (id !== undefined) deliveries?.expect(id, request.whenBusy);\n return response;\n });\n },\n rewind(messageId, context) {\n return serial(async () => {\n const conversation = current();\n const attached = await conversation.viewState(context);\n const view = attached.value;\n attached.dispose();\n const pending = (await harness.inspect(context)).submissions.some(\n (submission) => submission.conversationId === conversation.id\n );\n if (pending || busy(view)) return { done: false, text: null, reason: 'busy' };\n const turn = lastTurn(view, messageId);\n if (!turn) return { done: false, text: null, reason: 'no-turn' };\n const agent = view.docs['pi.agent'] as AgentChange | undefined;\n const ownership = { kind: 'ownerless' } as const;\n const fork =\n turn.at === null\n ? await harness.createConversation(\n {\n ownership,\n agent: {\n model: agent?.model ?? null,\n thinkingLevel: agent?.thinkingLevel ?? null,\n },\n },\n context\n )\n : await conversation.fork(turn.at, { ownership }, context);\n const changed =\n scoops && agents\n ? await scoops.scoops.rewound(agents.activeCone(), fork, context)\n : { stopped: [], restored: [] };\n await fork.submit(\n { type: 'write', entry: { kind: 'slicc.rewound', data: { turns: 1, ...changed } } },\n context\n );\n await cone.switchCone(fork, context);\n return { done: true, text: turn.text, reason: null };\n });\n },\n\n async wait(id, context) {\n const parsed = submissionId(id);\n const submission =\n parsed === undefined ? undefined : await harness.submission(parsed, context);\n if (!submission) throw new Error(`unknown submission ${id}`);\n const settled = await submission.wait(context);\n const { answer, reason } = settled as { answer?: EntryId; reason?: string };\n if (answer === undefined) return { status: 'unanswered', text: null, reason: String(reason) };\n const entry = await harness.commit((tx) => tx.entry(AssistantEntry, answer), context);\n return { status: 'done', text: answerText(entry?.model?.[0]?.content), reason: null };\n },\n withdraw: (id, context) => withdraw(current(), id, context),\n async unqueue(agentId, id, context) {\n return withdraw(await conversationFor(agentId, context), id, context);\n },\n abort: (context) => current().abort(context),\n compact: (instructions, context) =>\n accepted(() => current().compact(instructions ?? undefined, context)),\n reset: (handoff, context) => current().reset(handoff ?? undefined, context),\n resolveLick(lickId, state, context) {\n return serial(async () => {\n if (!licks) return { done: false, text: null, error: 'this agent has no licks' };\n const conversation = current();\n const action = state === 'confirmed' ? 'confirm' : 'dismiss';\n try {\n const text = await licks.licks.decide(conversation, lickId, action, undefined, context);\n await conversation.submit(\n {\n type: 'write',\n entry: {\n kind: LICK_STATE_KIND,\n data: { lick: lickId, state, by: 'user' },\n model: [\n {\n role: 'user',\n content: `The user ${state} lick ${lickId}: ${text}`,\n timestamp: Date.now(),\n },\n ],\n },\n },\n context\n );\n return { done: true, text, error: null };\n } catch (error) {\n return { done: false, text: null, error: (error as Error).message };\n }\n });\n },\n async webhook(name, delivery, context) {\n const delivered = licks\n ? await licks.sources.webhook(\n name,\n {\n ...(delivery.id ? { id: delivery.id } : {}),\n headers: delivery.headers,\n body: delivery.body,\n },\n context\n )\n : false;\n return { delivered };\n },\n async stopAgent(agentId, context) {\n await (await conversationFor(agentId, context)).abort(context);\n },\n async selectCone(agentId, context) {\n await agents?.selectCone(agentId, context);\n },\n createCone(name, context) {\n return created(async () => {\n if (!agents) throw new Error('This agent has a single cone.');\n return agents.createCone(name, context);\n });\n },\n createScoop(parentId, name, context) {\n return created(async () => {\n if (!scoops) throw new Error('This agent has no scoops.');\n const roles = await scoops.runtime.roles(context);\n const answer = await scoops.scoops.spawn(\n { cone: parentId, name, prompts: [], fromAgent: false, limits: roles.limits },\n context\n );\n if (answer.code !== 0) throw new Error(readable(answer.out));\n return `scoop:${answer.out.trim()}`;\n });\n },\n drop(agentId, context) {\n return created(async () => {\n if (!scoops) throw new Error('This agent has no scoops.');\n const answer = await scoops.scoops.stop(agentId, false, context);\n if (answer.code !== 0) throw new Error(readable(answer.out));\n return agentId;\n });\n },\n configure: async (change, context) =>\n (await conversationFor(change.agentId, context)).configure(\n {\n ...(change.model ? { model: change.model } : {}),\n ...(change.thinkingLevel ? { thinkingLevel: change.thinkingLevel } : {}),\n },\n context\n ),\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/host.js
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
AgentSettings,
|
|
19
19
|
AgentTranscript
|
|
20
20
|
} from "./services.js";
|
|
21
|
+
import { agentViews } from "./views.js";
|
|
21
22
|
function endpointFor(provider) {
|
|
22
23
|
const endpoint = createRemoteServiceEndpoint(provider);
|
|
23
24
|
return {
|
|
@@ -87,6 +88,7 @@ async function hostAgent(agent, options = {}) {
|
|
|
87
88
|
const serverId = options.serverId ?? crypto.randomUUID();
|
|
88
89
|
const transcript = await follow(agent, context);
|
|
89
90
|
const { state } = transcript;
|
|
91
|
+
const mounted = await agentViews(agent, context);
|
|
90
92
|
const provider = new RemoteServiceProvider([
|
|
91
93
|
{ service: AgentControl, mode: "singleton" },
|
|
92
94
|
{ service: AgentTranscript, mode: "singleton" },
|
|
@@ -95,9 +97,14 @@ async function hostAgent(agent, options = {}) {
|
|
|
95
97
|
const deliveries = trackDeliveries(agent.harness, state, context);
|
|
96
98
|
provider.provide(
|
|
97
99
|
AgentControl,
|
|
98
|
-
createAgentControl(agent.harness, agent, deliveries, options.licks)
|
|
100
|
+
createAgentControl(agent.harness, agent, deliveries, options.licks, options.scoops)
|
|
99
101
|
);
|
|
100
|
-
provider.provide(AgentTranscript, {
|
|
102
|
+
provider.provide(AgentTranscript, {
|
|
103
|
+
state,
|
|
104
|
+
deliveries: deliveries.state,
|
|
105
|
+
agents: mounted.agents,
|
|
106
|
+
views: mounted.views
|
|
107
|
+
});
|
|
101
108
|
provider.provide(AgentSettings, options.settings ?? noSettings);
|
|
102
109
|
const host = {
|
|
103
110
|
serverServices: serverServices(),
|
|
@@ -119,6 +126,8 @@ async function hostAgent(agent, options = {}) {
|
|
|
119
126
|
provider.dispose();
|
|
120
127
|
deliveries.dispose();
|
|
121
128
|
transcript.dispose();
|
|
129
|
+
mounted.dispose();
|
|
130
|
+
await options.scoops?.runtime.close(context);
|
|
122
131
|
await options.licks?.sources.close(context);
|
|
123
132
|
await agent.close();
|
|
124
133
|
}
|
package/dist/host.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/host.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n type Context,\n createRemoteServiceEndpoint,\n RemoteServiceProvider,\n replicatedState,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport {\n type RoutedServerServiceHost,\n type RoutedSessionAttachment,\n type RoutedSessionHandle,\n Server,\n type ServerHost,\n SessionNotFoundError,\n} from '@earendil-works/pi-server';\nimport type { Agent } from './agent.ts';\nimport { createAgentControl, type HostLicks } from './control.ts';\nimport { trackDeliveries } from './deliveries.ts';\nimport { PortListener } from './listener.ts';\nimport {\n AGENT_SESSION,\n AgentControl,\n AgentSessions,\n AgentSettings,\n AgentTranscript,\n} from './services.ts';\nimport type { PortEndpoint } from './wire.ts';\n\nexport interface AgentHost {\n readonly serverId: string;\n readonly agent: Agent;\n connect(endpoint: PortEndpoint): void;\n close(): Promise<void>;\n}\n\nfunction endpointFor(provider: RemoteServiceProvider): RoutedSessionAttachment {\n const endpoint = createRemoteServiceEndpoint(provider);\n return {\n invokeService: (call, publish, context) =>\n endpoint.invoke(call, (id, update) => publish(id, update, context), context),\n release: () => endpoint.dispose(),\n };\n}\n\nfunction sessionServices(provider: RemoteServiceProvider): RoutedSessionHandle {\n return {\n attachClient: () => endpointFor(provider),\n close: async () => {},\n };\n}\n\nfunction serverServices(): RoutedServerServiceHost {\n return {\n attachClient(presentation) {\n const provider = new RemoteServiceProvider([{ service: AgentSessions, mode: 'singleton' }]);\n provider.provide(AgentSessions, {\n attach: (sessionId, context) => presentation.attachSession(sessionId, context),\n detach: (context) => presentation.detachSession(context),\n });\n const attachment = endpointFor(provider);\n return {\n invokeService: attachment.invokeService,\n release(context) {\n attachment.release(context);\n provider.dispose();\n },\n };\n },\n };\n}\n\nconst noSettings: AgentSettings = {\n state: replicatedState({ models: [], accounts: [] }),\n connect: async () => {\n throw new Error('this agent has no accounts');\n },\n disconnect: async () => {},\n signIn: async () => null,\n};\n\nasync function follow(agent: Agent, context: Context) {\n let attached = await agent.cone().viewState(context);\n const state = replicatedState<ConversationView>(attached.value);\n const mirror = (value: ConversationView, from: Context) => state.replace(from, value);\n let off = attached.subscribe(mirror);\n const stop = agent.onCone(async (conversation) => {\n const next = await conversation.viewState(context);\n off();\n attached.dispose();\n attached = next;\n state.replace(context, next.value);\n off = next.subscribe(mirror);\n });\n return {\n state,\n dispose() {\n stop();\n off();\n attached.dispose();\n },\n };\n}\n\nexport async function hostAgent(\n agent: Agent,\n options: {\n serverId?: string;\n context?: Context;\n settings?: AgentSettings;\n licks?: HostLicks;\n } = {}\n): Promise<AgentHost> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const serverId = options.serverId ?? crypto.randomUUID();\n const transcript = await follow(agent, context);\n const { state } = transcript;\n const provider = new RemoteServiceProvider([\n { service: AgentControl, mode: 'singleton' },\n { service: AgentTranscript, mode: 'singleton' },\n { service: AgentSettings, mode: 'singleton' },\n ]);\n const deliveries = trackDeliveries(agent.harness, state, context);\n provider.provide(\n AgentControl,\n createAgentControl(agent.harness, agent, deliveries, options.licks)\n );\n provider.provide(AgentTranscript, {
|
|
5
|
-
"mappings": "AAAA;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,0BAA0B;AAEnC;AAAA,EAIE;AAAA,EAEA;AAAA,OACK;AAEP,SAAS,
|
|
4
|
+
"sourcesContent": ["import {\n type Context,\n createRemoteServiceEndpoint,\n RemoteServiceProvider,\n replicatedState,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport {\n type RoutedServerServiceHost,\n type RoutedSessionAttachment,\n type RoutedSessionHandle,\n Server,\n type ServerHost,\n SessionNotFoundError,\n} from '@earendil-works/pi-server';\nimport type { Agent } from './agent.ts';\nimport { createAgentControl, type HostLicks, type HostScoops } from './control.ts';\nimport { trackDeliveries } from './deliveries.ts';\nimport { PortListener } from './listener.ts';\nimport {\n AGENT_SESSION,\n AgentControl,\n AgentSessions,\n AgentSettings,\n AgentTranscript,\n} from './services.ts';\nimport { agentViews } from './views.ts';\nimport type { PortEndpoint } from './wire.ts';\n\nexport interface AgentHost {\n readonly serverId: string;\n readonly agent: Agent;\n connect(endpoint: PortEndpoint): void;\n close(): Promise<void>;\n}\n\nfunction endpointFor(provider: RemoteServiceProvider): RoutedSessionAttachment {\n const endpoint = createRemoteServiceEndpoint(provider);\n return {\n invokeService: (call, publish, context) =>\n endpoint.invoke(call, (id, update) => publish(id, update, context), context),\n release: () => endpoint.dispose(),\n };\n}\n\nfunction sessionServices(provider: RemoteServiceProvider): RoutedSessionHandle {\n return {\n attachClient: () => endpointFor(provider),\n close: async () => {},\n };\n}\n\nfunction serverServices(): RoutedServerServiceHost {\n return {\n attachClient(presentation) {\n const provider = new RemoteServiceProvider([{ service: AgentSessions, mode: 'singleton' }]);\n provider.provide(AgentSessions, {\n attach: (sessionId, context) => presentation.attachSession(sessionId, context),\n detach: (context) => presentation.detachSession(context),\n });\n const attachment = endpointFor(provider);\n return {\n invokeService: attachment.invokeService,\n release(context) {\n attachment.release(context);\n provider.dispose();\n },\n };\n },\n };\n}\n\nconst noSettings: AgentSettings = {\n state: replicatedState({ models: [], accounts: [] }),\n connect: async () => {\n throw new Error('this agent has no accounts');\n },\n disconnect: async () => {},\n signIn: async () => null,\n};\n\nasync function follow(agent: Agent, context: Context) {\n let attached = await agent.cone().viewState(context);\n const state = replicatedState<ConversationView>(attached.value);\n const mirror = (value: ConversationView, from: Context) => state.replace(from, value);\n let off = attached.subscribe(mirror);\n const stop = agent.onCone(async (conversation) => {\n const next = await conversation.viewState(context);\n off();\n attached.dispose();\n attached = next;\n state.replace(context, next.value);\n off = next.subscribe(mirror);\n });\n return {\n state,\n dispose() {\n stop();\n off();\n attached.dispose();\n },\n };\n}\n\nexport async function hostAgent(\n agent: Agent,\n options: {\n serverId?: string;\n context?: Context;\n settings?: AgentSettings;\n licks?: HostLicks;\n scoops?: HostScoops;\n } = {}\n): Promise<AgentHost> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const serverId = options.serverId ?? crypto.randomUUID();\n const transcript = await follow(agent, context);\n const { state } = transcript;\n const mounted = await agentViews(agent, context);\n const provider = new RemoteServiceProvider([\n { service: AgentControl, mode: 'singleton' },\n { service: AgentTranscript, mode: 'singleton' },\n { service: AgentSettings, mode: 'singleton' },\n ]);\n const deliveries = trackDeliveries(agent.harness, state, context);\n provider.provide(\n AgentControl,\n createAgentControl(agent.harness, agent, deliveries, options.licks, options.scoops)\n );\n provider.provide(AgentTranscript, {\n state,\n deliveries: deliveries.state,\n agents: mounted.agents,\n views: mounted.views,\n });\n provider.provide(AgentSettings, options.settings ?? noSettings);\n const host: ServerHost = {\n serverServices: serverServices(),\n async resolveSession(sessionId) {\n if (sessionId !== AGENT_SESSION)\n throw new SessionNotFoundError(`unknown session ${sessionId}`);\n return { id: sessionId };\n },\n openSession: async () => sessionServices(provider),\n };\n const listener = new PortListener(serverId);\n const server = await new Server(host, { serverId, listeners: [listener] }).start();\n return {\n serverId,\n agent,\n connect: (endpoint) => listener.connect(endpoint),\n async close() {\n await server.close();\n provider.dispose();\n deliveries.dispose();\n transcript.dispose();\n mounted.dispose();\n await options.scoops?.runtime.close(context);\n await options.licks?.sources.close(context);\n await agent.close();\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/kernel/env.js
CHANGED
|
@@ -6,7 +6,15 @@ import {
|
|
|
6
6
|
import { execute } from "./exec.js";
|
|
7
7
|
import { attempt, info, KernelDirReader, SnapshotLines, SnapshotReader } from "./files.js";
|
|
8
8
|
import { dirname, normalize, resolve } from "./paths.js";
|
|
9
|
-
import { pollWatch } from "./watch.js";
|
|
9
|
+
import { nativeWatch, pollWatch, RESCAN_MS } from "./watch.js";
|
|
10
|
+
function quote(value) {
|
|
11
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
12
|
+
}
|
|
13
|
+
function prefixed(command, exports) {
|
|
14
|
+
const names = Object.keys(exports ?? {}).filter((name) => /^[A-Z_][A-Z0-9_]*$/.test(name));
|
|
15
|
+
if (!names.length) return command;
|
|
16
|
+
return `export ${names.map((name) => `${name}=${quote(exports[name])}`).join(" ")}; ${command}`;
|
|
17
|
+
}
|
|
10
18
|
class SliccKernelEnv {
|
|
11
19
|
id;
|
|
12
20
|
cwd;
|
|
@@ -14,12 +22,18 @@ class SliccKernelEnv {
|
|
|
14
22
|
#watchers = /* @__PURE__ */ new Set();
|
|
15
23
|
#poll;
|
|
16
24
|
#activity;
|
|
25
|
+
#groups;
|
|
26
|
+
#owner;
|
|
27
|
+
#exports;
|
|
17
28
|
constructor(client, options) {
|
|
18
29
|
this.#client = client;
|
|
19
30
|
this.cwd = options.cwd;
|
|
20
31
|
this.id = options.id ?? "slicc-kernel";
|
|
21
32
|
this.#poll = options.watch ?? {};
|
|
22
33
|
this.#activity = options.activity;
|
|
34
|
+
this.#groups = options.groups;
|
|
35
|
+
this.#owner = options.owner;
|
|
36
|
+
this.#exports = options.exports;
|
|
23
37
|
}
|
|
24
38
|
#path(path) {
|
|
25
39
|
return resolve(this.cwd, path);
|
|
@@ -138,7 +152,24 @@ class SliccKernelEnv {
|
|
|
138
152
|
}
|
|
139
153
|
watch(targets, onChange, context) {
|
|
140
154
|
return attempt(context, this.cwd, async () => {
|
|
141
|
-
|
|
155
|
+
let absorb = () => void 0;
|
|
156
|
+
const reported = (change) => {
|
|
157
|
+
if ("paths" in change) absorb(change.paths);
|
|
158
|
+
onChange(change);
|
|
159
|
+
};
|
|
160
|
+
const native = this.#poll.mode === "polling" ? void 0 : await nativeWatch(this.#fs, this.cwd, targets, reported);
|
|
161
|
+
const intervalMs = native ? this.#poll.rescanMs ?? RESCAN_MS : this.#poll.intervalMs;
|
|
162
|
+
const polled = await pollWatch(this.#fs, this.cwd, targets, onChange, {
|
|
163
|
+
...intervalMs === void 0 ? {} : { intervalMs }
|
|
164
|
+
});
|
|
165
|
+
absorb = (paths) => polled.absorb(paths);
|
|
166
|
+
const watcher = native ? {
|
|
167
|
+
mode: "native",
|
|
168
|
+
async close(closing) {
|
|
169
|
+
await native.close(closing);
|
|
170
|
+
await polled.close(closing);
|
|
171
|
+
}
|
|
172
|
+
} : polled;
|
|
142
173
|
this.#watchers.add(watcher);
|
|
143
174
|
return {
|
|
144
175
|
mode: watcher.mode,
|
|
@@ -193,7 +224,11 @@ class SliccKernelEnv {
|
|
|
193
224
|
}
|
|
194
225
|
exec(command, options, context) {
|
|
195
226
|
const ended = this.#activity?.began();
|
|
196
|
-
|
|
227
|
+
const groups = this.#groups;
|
|
228
|
+
const owner = this.#owner;
|
|
229
|
+
const spawned = groups && owner !== void 0 ? (process) => groups.track(owner, process) : void 0;
|
|
230
|
+
const line = typeof command === "string" ? prefixed(command, this.#exports) : command;
|
|
231
|
+
return execute(this.#client, this.cwd, line, options, context, spawned).finally(ended);
|
|
197
232
|
}
|
|
198
233
|
async cleanup(context) {
|
|
199
234
|
for (const watcher of [...this.#watchers]) await watcher.close(context);
|
|
@@ -202,11 +237,20 @@ class SliccKernelEnv {
|
|
|
202
237
|
}
|
|
203
238
|
const HOME = "/home";
|
|
204
239
|
function kernelEnvironment(client, options = {}) {
|
|
205
|
-
|
|
240
|
+
const { identify, ...rest } = options;
|
|
241
|
+
const make = (target, exports) => new SliccKernelEnv(client, {
|
|
242
|
+
...rest,
|
|
243
|
+
cwd: target.cwd ?? HOME,
|
|
244
|
+
...target.conversationId === void 0 ? {} : { owner: target.conversationId },
|
|
245
|
+
...exports ? { exports } : {}
|
|
246
|
+
});
|
|
247
|
+
return (target, context) => identify && context ? identify(target, context).then((exports) => make(target, exports)) : make(target);
|
|
206
248
|
}
|
|
207
249
|
export {
|
|
208
250
|
HOME,
|
|
209
251
|
SliccKernelEnv,
|
|
210
|
-
kernelEnvironment
|
|
252
|
+
kernelEnvironment,
|
|
253
|
+
prefixed,
|
|
254
|
+
quote
|
|
211
255
|
};
|
|
212
256
|
//# sourceMappingURL=env.js.map
|
package/dist/kernel/env.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/kernel/env.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type BinaryReader,\n type DirReader,\n type ExecutionEnv,\n type ExecutionError,\n err,\n FileError,\n type FileInfo,\n type FileWatcher,\n ok,\n type Result,\n type ShellExecOptions,\n type ShellExecResult,\n type TextLineReader,\n type WatchChange,\n type WatchTarget,\n} from '@earendil-works/pi-durable/env';\nimport type { Activity } from './activity.ts';\nimport type { KernelClient } from './client.ts';\nimport { execute } from './exec.ts';\nimport { attempt, info, KernelDirReader, SnapshotLines, SnapshotReader } from './files.ts';\nimport { dirname, normalize, resolve } from './paths.ts';\nimport { type PollOptions, pollWatch } from './watch.ts';\n\nexport interface KernelEnvOptions {\n cwd: string;\n id?: string;\n watch?: PollOptions;\n activity?: Activity;\n}\n\ntype FileResult<T> = Promise<Result<T, FileError>>;\n\nexport class SliccKernelEnv implements ExecutionEnv {\n readonly id: string;\n cwd: string;\n readonly #client: KernelClient;\n readonly #watchers = new Set<FileWatcher>();\n readonly #poll: PollOptions;\n readonly #activity: Activity | undefined;\n\n constructor(client: KernelClient, options: KernelEnvOptions) {\n this.#client = client;\n this.cwd = options.cwd;\n this.id = options.id ?? 'slicc-kernel';\n this.#poll = options.watch ?? {};\n this.#activity = options.activity;\n }\n\n #path(path: string): string {\n return resolve(this.cwd, path);\n }\n\n #written(path: string): string {\n const resolved = this.#path(path);\n this.#activity?.wrote(resolved);\n return resolved;\n }\n\n get #fs() {\n return this.#client.fs;\n }\n\n async absolutePath(path: string): FileResult<string> {\n return ok(this.#path(path));\n }\n\n async joinPath(parts: string[]): FileResult<string> {\n return ok(normalize(parts.join('/')));\n }\n\n readTextFile(path: string, context: Context): FileResult<string> {\n const resolved = this.#path(path);\n return attempt(context, resolved, () => this.#fs.readText(resolved));\n }\n\n readBinaryFile(path: string, context: Context): FileResult<Uint8Array> {\n const resolved = this.#path(path);\n return attempt(context, resolved, () => this.#fs.readFile(resolved));\n }\n\n async openTextLineReader(path: string, context: Context): FileResult<TextLineReader> {\n const text = await this.readTextFile(path, context);\n return text.ok ? ok(new SnapshotLines(text.value)) : text;\n }\n\n async readTextLines(\n path: string,\n options: { maxLines?: number } | undefined,\n context: Context\n ): FileResult<string[]> {\n if (options?.maxLines !== undefined && options.maxLines <= 0) return ok([]);\n const text = await this.readTextFile(path, context);\n if (!text.ok) return text;\n const reader = new SnapshotLines(text.value);\n const lines: string[] = [];\n for (\n let line = await reader.readLine();\n line.ok && line.value;\n line = await reader.readLine()\n ) {\n if (options?.maxLines !== undefined && lines.length >= options.maxLines) break;\n lines.push(line.value.text);\n }\n return ok(lines);\n }\n\n openBinaryReader(\n path: string,\n options: { noFollow?: boolean } | undefined,\n context: Context\n ): FileResult<BinaryReader> {\n const resolved = this.#path(path);\n return attempt(context, resolved, async () => {\n if (options?.noFollow && (await this.#fs.lstat(resolved)).isSymbolicLink) {\n throw new FileError('invalid', 'Refusing to follow a symbolic link', resolved);\n }\n const stat = await this.#fs.stat(resolved);\n if (stat.isDirectory) throw new FileError('is_directory', 'Is a directory', resolved);\n if (!stat.isFile) throw new FileError('invalid', 'Not a regular file', resolved);\n return new SnapshotReader(await this.#fs.readFile(resolved), info(resolved, stat));\n });\n }\n\n writeFile(path: string, content: string | Uint8Array, context: Context): FileResult<void> {\n const resolved = this.#written(path);\n return attempt(context, resolved, async () => {\n await this.#fs.mkdir(dirname(resolved));\n await this.#fs.writeFile(resolved, content);\n });\n }\n\n appendFile(path: string, content: string | Uint8Array, context: Context): FileResult<void> {\n const resolved = this.#written(path);\n return attempt(context, resolved, async () => {\n const before = (await this.#fs.exists(resolved))\n ? await this.#fs.readFile(resolved)\n : new Uint8Array();\n const added = typeof content === 'string' ? new TextEncoder().encode(content) : content;\n const all = new Uint8Array(before.length + added.length);\n all.set(before);\n all.set(added, before.length);\n await this.#fs.mkdir(dirname(resolved));\n await this.#fs.writeFile(resolved, all);\n });\n }\n\n truncateFile(path: string, size: number, context: Context): FileResult<void> {\n const resolved = this.#written(path);\n if (!Number.isSafeInteger(size) || size < 0) {\n return Promise.resolve(\n err(new FileError('invalid', 'File size must be a non-negative integer', resolved))\n );\n }\n return attempt(context, resolved, async () => {\n const before = await this.#fs.readFile(resolved);\n const after = new Uint8Array(size);\n after.set(before.subarray(0, size));\n await this.#fs.writeFile(resolved, after);\n });\n }\n\n flushFile(path: string, context: Context): FileResult<void> {\n const resolved = this.#path(path);\n return attempt(context, resolved, async () => {\n if ((await this.#fs.stat(resolved)).isDirectory)\n throw Object.assign(new Error('Is a directory'), { code: 'EISDIR' });\n });\n }\n\n renameFile(sourcePath: string, destinationPath: string, context: Context): FileResult<void> {\n const source = this.#written(sourcePath);\n return attempt(context, source, () => this.#fs.rename(source, this.#written(destinationPath)));\n }\n\n fileInfo(path: string, context: Context): FileResult<FileInfo> {\n const resolved = this.#path(path);\n return attempt(context, resolved, async () => info(resolved, await this.#fs.lstat(resolved)));\n }\n\n async listDir(path: string, context: Context): FileResult<FileInfo[]> {\n const reader = await this.openDirReader(path, context);\n if (!reader.ok) return reader;\n return ok(await (reader.value as KernelDirReader).rest());\n }\n\n openDirReader(path: string, context: Context): FileResult<DirReader> {\n const resolved = this.#path(path);\n return attempt(context, resolved, async () => {\n if (!(await this.#fs.stat(resolved)).isDirectory) {\n throw Object.assign(new Error('Not a directory'), { code: 'ENOTDIR' });\n }\n return new KernelDirReader(this.#fs, resolved, await this.#fs.readdir(resolved));\n });\n }\n\n watch(\n targets: readonly WatchTarget[],\n onChange: (change: WatchChange) => void,\n context: Context\n ): FileResult<FileWatcher> {\n return attempt(context, this.cwd, async () => {\n const watcher = await pollWatch(this.#fs, this.cwd, targets, onChange, this.#poll);\n this.#watchers.add(watcher);\n return {\n mode: watcher.mode,\n close: async (closing: Context) => {\n this.#watchers.delete(watcher);\n await watcher.close(closing);\n },\n };\n });\n }\n\n canonicalPath(path: string, context: Context): FileResult<string> {\n const resolved = this.#path(path);\n return attempt(context, resolved, () => this.#fs.realpath(resolved));\n }\n\n async exists(path: string, context: Context): FileResult<boolean> {\n const found = await this.fileInfo(path, context);\n if (found.ok) return ok(true);\n return found.error.code === 'not_found' ? ok(false) : found;\n }\n\n createDir(\n path: string,\n _options: { recursive?: boolean } | undefined,\n context: Context\n ): FileResult<void> {\n const resolved = this.#written(path);\n return attempt(context, resolved, () => this.#fs.mkdir(resolved));\n }\n\n remove(\n path: string,\n options: { recursive?: boolean; force?: boolean } | undefined,\n context: Context\n ): FileResult<void> {\n const resolved = this.#written(path);\n return attempt(context, resolved, async () => {\n if (!options?.recursive) {\n const stat = await this.#fs.lstat(resolved).catch((error: unknown) => {\n if (options?.force) return undefined;\n throw error;\n });\n if (stat?.isDirectory) throw Object.assign(new Error('Is a directory'), { code: 'EISDIR' });\n }\n await this.#fs.rm(resolved, { force: options?.force ?? false });\n });\n }\n\n async createTempDir(prefix: string | undefined, context: Context): FileResult<string> {\n const path = `/tmp/${prefix ?? 'tmp-'}${crypto.randomUUID()}`;\n return attempt(context, path, async () => {\n await this.#fs.mkdir(path);\n return path;\n });\n }\n\n async createTempFile(\n options: { prefix?: string; suffix?: string } | undefined,\n context: Context\n ): FileResult<string> {\n const dir = await this.createTempDir('tmp-', context);\n if (!dir.ok) return dir;\n const path = `${dir.value}/${options?.prefix ?? ''}${crypto.randomUUID()}${options?.suffix ?? ''}`;\n return attempt(context, path, async () => {\n await this.#fs.writeFile(path, '');\n return path;\n });\n }\n\n exec(\n command: string | readonly string[],\n options: ShellExecOptions | undefined,\n context: Context\n ): Promise<Result<ShellExecResult, ExecutionError>> {\n const ended = this.#activity?.began();\n return execute(this.#client, this.cwd, command, options, context).finally(ended);\n }\n\n async cleanup(context: Context): Promise<void> {\n for (const watcher of [...this.#watchers]) await watcher.close(context);\n this.#watchers.clear();\n }\n}\n\nexport const HOME = '/home';\n\nexport function kernelEnvironment(\n client: KernelClient,\n options: Omit<KernelEnvOptions, 'cwd'> = {}\n): (target: { cwd?: string }) => SliccKernelEnv {\n return (target) => new SliccKernelEnv(client, { ...options, cwd: target.cwd ?? HOME });\n}\n"],
|
|
5
|
-
"mappings": "AACA;AAAA,EAKE;AAAA,EACA;AAAA,EAGA;AAAA,OAOK;AAGP,SAAS,eAAe;AACxB,SAAS,SAAS,MAAM,iBAAiB,eAAe,sBAAsB;
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type BinaryReader,\n type DirReader,\n type ExecutionEnv,\n type ExecutionError,\n err,\n FileError,\n type FileInfo,\n type FileWatcher,\n ok,\n type Result,\n type ShellExecOptions,\n type ShellExecResult,\n type TextLineReader,\n type WatchChange,\n type WatchTarget,\n} from '@earendil-works/pi-durable/env';\nimport type { Activity } from './activity.ts';\nimport type { KernelClient, KernelProcess } from './client.ts';\nimport { execute } from './exec.ts';\nimport { attempt, info, KernelDirReader, SnapshotLines, SnapshotReader } from './files.ts';\nimport type { ProcessGroups } from './groups.ts';\nimport { dirname, normalize, resolve } from './paths.ts';\nimport { nativeWatch, type PollOptions, pollWatch, RESCAN_MS } from './watch.ts';\n\nexport interface KernelEnvOptions {\n cwd: string;\n id?: string;\n watch?: PollOptions;\n activity?: Activity;\n groups?: ProcessGroups;\n owner?: number;\n exports?: Readonly<Record<string, string>>;\n}\n\nexport function quote(value: string): string {\n return `'${value.replace(/'/g, \"'\\\\''\")}'`;\n}\n\nexport function prefixed(\n command: string,\n exports: Readonly<Record<string, string>> | undefined\n): string {\n const names = Object.keys(exports ?? {}).filter((name) => /^[A-Z_][A-Z0-9_]*$/.test(name));\n if (!names.length) return command;\n return `export ${names.map((name) => `${name}=${quote((exports as Record<string, string>)[name] as string)}`).join(' ')}; ${command}`;\n}\n\ntype FileResult<T> = Promise<Result<T, FileError>>;\n\nexport class SliccKernelEnv implements ExecutionEnv {\n readonly id: string;\n cwd: string;\n readonly #client: KernelClient;\n readonly #watchers = new Set<FileWatcher>();\n readonly #poll: PollOptions;\n readonly #activity: Activity | undefined;\n readonly #groups: ProcessGroups | undefined;\n readonly #owner: number | undefined;\n readonly #exports: Readonly<Record<string, string>> | undefined;\n\n constructor(client: KernelClient, options: KernelEnvOptions) {\n this.#client = client;\n this.cwd = options.cwd;\n this.id = options.id ?? 'slicc-kernel';\n this.#poll = options.watch ?? {};\n this.#activity = options.activity;\n this.#groups = options.groups;\n this.#owner = options.owner;\n this.#exports = options.exports;\n }\n\n #path(path: string): string {\n return resolve(this.cwd, path);\n }\n\n #written(path: string): string {\n const resolved = this.#path(path);\n this.#activity?.wrote(resolved);\n return resolved;\n }\n\n get #fs() {\n return this.#client.fs;\n }\n\n async absolutePath(path: string): FileResult<string> {\n return ok(this.#path(path));\n }\n\n async joinPath(parts: string[]): FileResult<string> {\n return ok(normalize(parts.join('/')));\n }\n\n readTextFile(path: string, context: Context): FileResult<string> {\n const resolved = this.#path(path);\n return attempt(context, resolved, () => this.#fs.readText(resolved));\n }\n\n readBinaryFile(path: string, context: Context): FileResult<Uint8Array> {\n const resolved = this.#path(path);\n return attempt(context, resolved, () => this.#fs.readFile(resolved));\n }\n\n async openTextLineReader(path: string, context: Context): FileResult<TextLineReader> {\n const text = await this.readTextFile(path, context);\n return text.ok ? ok(new SnapshotLines(text.value)) : text;\n }\n\n async readTextLines(\n path: string,\n options: { maxLines?: number } | undefined,\n context: Context\n ): FileResult<string[]> {\n if (options?.maxLines !== undefined && options.maxLines <= 0) return ok([]);\n const text = await this.readTextFile(path, context);\n if (!text.ok) return text;\n const reader = new SnapshotLines(text.value);\n const lines: string[] = [];\n for (\n let line = await reader.readLine();\n line.ok && line.value;\n line = await reader.readLine()\n ) {\n if (options?.maxLines !== undefined && lines.length >= options.maxLines) break;\n lines.push(line.value.text);\n }\n return ok(lines);\n }\n\n openBinaryReader(\n path: string,\n options: { noFollow?: boolean } | undefined,\n context: Context\n ): FileResult<BinaryReader> {\n const resolved = this.#path(path);\n return attempt(context, resolved, async () => {\n if (options?.noFollow && (await this.#fs.lstat(resolved)).isSymbolicLink) {\n throw new FileError('invalid', 'Refusing to follow a symbolic link', resolved);\n }\n const stat = await this.#fs.stat(resolved);\n if (stat.isDirectory) throw new FileError('is_directory', 'Is a directory', resolved);\n if (!stat.isFile) throw new FileError('invalid', 'Not a regular file', resolved);\n return new SnapshotReader(await this.#fs.readFile(resolved), info(resolved, stat));\n });\n }\n\n writeFile(path: string, content: string | Uint8Array, context: Context): FileResult<void> {\n const resolved = this.#written(path);\n return attempt(context, resolved, async () => {\n await this.#fs.mkdir(dirname(resolved));\n await this.#fs.writeFile(resolved, content);\n });\n }\n\n appendFile(path: string, content: string | Uint8Array, context: Context): FileResult<void> {\n const resolved = this.#written(path);\n return attempt(context, resolved, async () => {\n const before = (await this.#fs.exists(resolved))\n ? await this.#fs.readFile(resolved)\n : new Uint8Array();\n const added = typeof content === 'string' ? new TextEncoder().encode(content) : content;\n const all = new Uint8Array(before.length + added.length);\n all.set(before);\n all.set(added, before.length);\n await this.#fs.mkdir(dirname(resolved));\n await this.#fs.writeFile(resolved, all);\n });\n }\n\n truncateFile(path: string, size: number, context: Context): FileResult<void> {\n const resolved = this.#written(path);\n if (!Number.isSafeInteger(size) || size < 0) {\n return Promise.resolve(\n err(new FileError('invalid', 'File size must be a non-negative integer', resolved))\n );\n }\n return attempt(context, resolved, async () => {\n const before = await this.#fs.readFile(resolved);\n const after = new Uint8Array(size);\n after.set(before.subarray(0, size));\n await this.#fs.writeFile(resolved, after);\n });\n }\n\n flushFile(path: string, context: Context): FileResult<void> {\n const resolved = this.#path(path);\n return attempt(context, resolved, async () => {\n if ((await this.#fs.stat(resolved)).isDirectory)\n throw Object.assign(new Error('Is a directory'), { code: 'EISDIR' });\n });\n }\n\n renameFile(sourcePath: string, destinationPath: string, context: Context): FileResult<void> {\n const source = this.#written(sourcePath);\n return attempt(context, source, () => this.#fs.rename(source, this.#written(destinationPath)));\n }\n\n fileInfo(path: string, context: Context): FileResult<FileInfo> {\n const resolved = this.#path(path);\n return attempt(context, resolved, async () => info(resolved, await this.#fs.lstat(resolved)));\n }\n\n async listDir(path: string, context: Context): FileResult<FileInfo[]> {\n const reader = await this.openDirReader(path, context);\n if (!reader.ok) return reader;\n return ok(await (reader.value as KernelDirReader).rest());\n }\n\n openDirReader(path: string, context: Context): FileResult<DirReader> {\n const resolved = this.#path(path);\n return attempt(context, resolved, async () => {\n if (!(await this.#fs.stat(resolved)).isDirectory) {\n throw Object.assign(new Error('Not a directory'), { code: 'ENOTDIR' });\n }\n return new KernelDirReader(this.#fs, resolved, await this.#fs.readdir(resolved));\n });\n }\n\n watch(\n targets: readonly WatchTarget[],\n onChange: (change: WatchChange) => void,\n context: Context\n ): FileResult<FileWatcher> {\n return attempt(context, this.cwd, async () => {\n let absorb: (paths: readonly string[]) => void = () => undefined;\n const reported = (change: WatchChange) => {\n if ('paths' in change) absorb(change.paths);\n onChange(change);\n };\n const native =\n this.#poll.mode === 'polling'\n ? undefined\n : await nativeWatch(this.#fs, this.cwd, targets, reported);\n const intervalMs = native ? (this.#poll.rescanMs ?? RESCAN_MS) : this.#poll.intervalMs;\n const polled = await pollWatch(this.#fs, this.cwd, targets, onChange, {\n ...(intervalMs === undefined ? {} : { intervalMs }),\n });\n absorb = (paths) => polled.absorb(paths);\n const watcher: FileWatcher = native\n ? {\n mode: 'native',\n async close(closing: Context) {\n await native.close(closing);\n await polled.close(closing);\n },\n }\n : polled;\n this.#watchers.add(watcher);\n return {\n mode: watcher.mode,\n close: async (closing: Context) => {\n this.#watchers.delete(watcher);\n await watcher.close(closing);\n },\n };\n });\n }\n\n canonicalPath(path: string, context: Context): FileResult<string> {\n const resolved = this.#path(path);\n return attempt(context, resolved, () => this.#fs.realpath(resolved));\n }\n\n async exists(path: string, context: Context): FileResult<boolean> {\n const found = await this.fileInfo(path, context);\n if (found.ok) return ok(true);\n return found.error.code === 'not_found' ? ok(false) : found;\n }\n\n createDir(\n path: string,\n _options: { recursive?: boolean } | undefined,\n context: Context\n ): FileResult<void> {\n const resolved = this.#written(path);\n return attempt(context, resolved, () => this.#fs.mkdir(resolved));\n }\n\n remove(\n path: string,\n options: { recursive?: boolean; force?: boolean } | undefined,\n context: Context\n ): FileResult<void> {\n const resolved = this.#written(path);\n return attempt(context, resolved, async () => {\n if (!options?.recursive) {\n const stat = await this.#fs.lstat(resolved).catch((error: unknown) => {\n if (options?.force) return undefined;\n throw error;\n });\n if (stat?.isDirectory) throw Object.assign(new Error('Is a directory'), { code: 'EISDIR' });\n }\n await this.#fs.rm(resolved, { force: options?.force ?? false });\n });\n }\n\n async createTempDir(prefix: string | undefined, context: Context): FileResult<string> {\n const path = `/tmp/${prefix ?? 'tmp-'}${crypto.randomUUID()}`;\n return attempt(context, path, async () => {\n await this.#fs.mkdir(path);\n return path;\n });\n }\n\n async createTempFile(\n options: { prefix?: string; suffix?: string } | undefined,\n context: Context\n ): FileResult<string> {\n const dir = await this.createTempDir('tmp-', context);\n if (!dir.ok) return dir;\n const path = `${dir.value}/${options?.prefix ?? ''}${crypto.randomUUID()}${options?.suffix ?? ''}`;\n return attempt(context, path, async () => {\n await this.#fs.writeFile(path, '');\n return path;\n });\n }\n\n exec(\n command: string | readonly string[],\n options: ShellExecOptions | undefined,\n context: Context\n ): Promise<Result<ShellExecResult, ExecutionError>> {\n const ended = this.#activity?.began();\n const groups = this.#groups;\n const owner = this.#owner;\n const spawned =\n groups && owner !== undefined\n ? (process: KernelProcess) => groups.track(owner, process)\n : undefined;\n const line = typeof command === 'string' ? prefixed(command, this.#exports) : command;\n return execute(this.#client, this.cwd, line, options, context, spawned).finally(ended);\n }\n\n async cleanup(context: Context): Promise<void> {\n for (const watcher of [...this.#watchers]) await watcher.close(context);\n this.#watchers.clear();\n }\n}\n\nexport const HOME = '/home';\n\nexport type EnvTargetLike = { cwd?: string; conversationId?: number; read?: unknown };\n\nexport function kernelEnvironment(\n client: KernelClient,\n options: Omit<KernelEnvOptions, 'cwd'> & {\n identify?: (target: EnvTargetLike, context: Context) => Promise<Record<string, string>>;\n } = {}\n): (target: EnvTargetLike, context?: Context) => SliccKernelEnv | Promise<SliccKernelEnv> {\n const { identify, ...rest } = options;\n const make = (target: EnvTargetLike, exports?: Record<string, string>) =>\n new SliccKernelEnv(client, {\n ...rest,\n cwd: target.cwd ?? HOME,\n ...(target.conversationId === undefined ? {} : { owner: target.conversationId }),\n ...(exports ? { exports } : {}),\n });\n return (target, context) =>\n identify && context\n ? identify(target, context).then((exports) => make(target, exports))\n : make(target);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/kernel/exec.js
CHANGED
|
@@ -72,7 +72,7 @@ function guard(process, options, context) {
|
|
|
72
72
|
}
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
|
-
async function execute(client, cwd, command, options, context) {
|
|
75
|
+
async function execute(client, cwd, command, options, context, spawned) {
|
|
76
76
|
const argv = typeof command === "string" ? [...SHELL, command] : [...command];
|
|
77
77
|
if (argv.length === 0) return err(new ExecutionError("spawn_error", "No program to run"));
|
|
78
78
|
if (context.abortSignal?.aborted)
|
|
@@ -92,6 +92,7 @@ async function execute(client, cwd, command, options, context) {
|
|
|
92
92
|
const message = error instanceof Error ? error.message : String(error);
|
|
93
93
|
return err(new ExecutionError("spawn_error", message));
|
|
94
94
|
}
|
|
95
|
+
spawned?.(process);
|
|
95
96
|
const watch = guard(process, options, context);
|
|
96
97
|
const exitCode = await process.exited.finally(() => watch.release());
|
|
97
98
|
out.end();
|
package/dist/kernel/exec.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/kernel/exec.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n ExecutionError,\n err,\n ok,\n type Result,\n type ShellExecOptions,\n type ShellExecResult,\n StreamDecoder,\n} from '@earendil-works/pi-durable/env';\nimport type { KernelClient, KernelProcess } from './client.ts';\nimport { resolve } from './paths.ts';\n\nexport const SHELL = ['bash', '-c'] as const;\n\nclass Spill {\n readonly #chunks: Uint8Array[] = [];\n readonly #limit: { afterBytes: number; afterLines: number } | undefined;\n #bytes = 0;\n #lines = 0;\n\n constructor(limit: ShellExecOptions['spill']) {\n this.#limit = limit;\n }\n\n add(chunk: Uint8Array): void {\n if (!this.#limit) return;\n this.#chunks.push(chunk);\n this.#bytes += chunk.length;\n for (const byte of chunk) if (byte === 10) this.#lines++;\n }\n\n get over(): boolean {\n const limit = this.#limit;\n return (\n limit !== undefined && (this.#bytes > limit.afterBytes || this.#lines > limit.afterLines)\n );\n }\n\n async write(client: KernelClient): Promise<string | undefined> {\n if (!this.over) return undefined;\n const path = `/tmp/slicc-agent-output-${crypto.randomUUID()}.log`;\n const all = new Uint8Array(this.#bytes);\n let offset = 0;\n for (const chunk of this.#chunks) {\n all.set(chunk, offset);\n offset += chunk.length;\n }\n await client.fs.mkdir('/tmp');\n await client.fs.writeFile(path, all);\n return path;\n }\n}\n\nfunction sink(\n stream: 'stdout' | 'stderr',\n spill: Spill,\n options: ShellExecOptions | undefined,\n context: Context\n) {\n const decoder = new StreamDecoder();\n return {\n push(bytes: Uint8Array) {\n spill.add(bytes);\n const text = decoder.decode(bytes);\n if (text) options?.onOutput?.(text, context, { stream });\n },\n end() {\n const text = decoder.decode();\n if (text) options?.onOutput?.(text, context, { stream });\n },\n };\n}\n\ntype Ending = 'timeout' | 'aborted' | undefined;\n\nfunction guard(process: KernelProcess, options: ShellExecOptions | undefined, context: Context) {\n let ending: Ending;\n const stop = (why: Ending) => {\n ending ??= why;\n process.signal('SIGKILL');\n };\n const timer =\n options?.timeout === undefined\n ? undefined\n : setTimeout(() => stop('timeout'), options.timeout * 1000);\n const onAbort = () => stop('aborted');\n context.abortSignal?.addEventListener('abort', onAbort, { once: true });\n if (context.abortSignal?.aborted) onAbort();\n return {\n get ending() {\n return ending;\n },\n release() {\n clearTimeout(timer);\n context.abortSignal?.removeEventListener('abort', onAbort);\n },\n };\n}\n\nexport async function execute(\n client: KernelClient,\n cwd: string,\n command: string | readonly string[],\n options: ShellExecOptions | undefined,\n context: Context\n): Promise<Result<ShellExecResult, ExecutionError>> {\n const argv = typeof command === 'string' ? [...SHELL, command] : [...command];\n if (argv.length === 0) return err(new ExecutionError('spawn_error', 'No program to run'));\n if (context.abortSignal?.aborted)\n return err(new ExecutionError('aborted', 'The command was aborted'));\n const spill = new Spill(options?.spill);\n const out = sink('stdout', spill, options, context);\n const errors = sink('stderr', spill, options, context);\n let process: KernelProcess;\n try {\n process = await client.spawn(argv, {\n cwd: resolve(cwd, options?.cwd ?? '.'),\n ...(options?.env ? { env: options.env } : {}),\n onStdout: (bytes) => out.push(bytes),\n onStderr: (bytes) => errors.push(bytes),\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return err(new ExecutionError('spawn_error', message));\n }\n const watch = guard(process, options, context);\n const exitCode = await process.exited.finally(() => watch.release());\n out.end();\n errors.end();\n const spillPath = await spill.write(client);\n const ending = watch.ending;\n if (ending) {\n const error = new ExecutionError(\n ending,\n `The command was ${ending === 'timeout' ? 'timed out' : 'aborted'}`\n );\n if (spillPath) error.spillPath = spillPath;\n return err(error);\n }\n return ok(spillPath ? { exitCode, spillPath } : { exitCode });\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n ExecutionError,\n err,\n ok,\n type Result,\n type ShellExecOptions,\n type ShellExecResult,\n StreamDecoder,\n} from '@earendil-works/pi-durable/env';\nimport type { KernelClient, KernelProcess } from './client.ts';\nimport { resolve } from './paths.ts';\n\nexport const SHELL = ['bash', '-c'] as const;\n\nclass Spill {\n readonly #chunks: Uint8Array[] = [];\n readonly #limit: { afterBytes: number; afterLines: number } | undefined;\n #bytes = 0;\n #lines = 0;\n\n constructor(limit: ShellExecOptions['spill']) {\n this.#limit = limit;\n }\n\n add(chunk: Uint8Array): void {\n if (!this.#limit) return;\n this.#chunks.push(chunk);\n this.#bytes += chunk.length;\n for (const byte of chunk) if (byte === 10) this.#lines++;\n }\n\n get over(): boolean {\n const limit = this.#limit;\n return (\n limit !== undefined && (this.#bytes > limit.afterBytes || this.#lines > limit.afterLines)\n );\n }\n\n async write(client: KernelClient): Promise<string | undefined> {\n if (!this.over) return undefined;\n const path = `/tmp/slicc-agent-output-${crypto.randomUUID()}.log`;\n const all = new Uint8Array(this.#bytes);\n let offset = 0;\n for (const chunk of this.#chunks) {\n all.set(chunk, offset);\n offset += chunk.length;\n }\n await client.fs.mkdir('/tmp');\n await client.fs.writeFile(path, all);\n return path;\n }\n}\n\nfunction sink(\n stream: 'stdout' | 'stderr',\n spill: Spill,\n options: ShellExecOptions | undefined,\n context: Context\n) {\n const decoder = new StreamDecoder();\n return {\n push(bytes: Uint8Array) {\n spill.add(bytes);\n const text = decoder.decode(bytes);\n if (text) options?.onOutput?.(text, context, { stream });\n },\n end() {\n const text = decoder.decode();\n if (text) options?.onOutput?.(text, context, { stream });\n },\n };\n}\n\ntype Ending = 'timeout' | 'aborted' | undefined;\n\nfunction guard(process: KernelProcess, options: ShellExecOptions | undefined, context: Context) {\n let ending: Ending;\n const stop = (why: Ending) => {\n ending ??= why;\n process.signal('SIGKILL');\n };\n const timer =\n options?.timeout === undefined\n ? undefined\n : setTimeout(() => stop('timeout'), options.timeout * 1000);\n const onAbort = () => stop('aborted');\n context.abortSignal?.addEventListener('abort', onAbort, { once: true });\n if (context.abortSignal?.aborted) onAbort();\n return {\n get ending() {\n return ending;\n },\n release() {\n clearTimeout(timer);\n context.abortSignal?.removeEventListener('abort', onAbort);\n },\n };\n}\n\nexport async function execute(\n client: KernelClient,\n cwd: string,\n command: string | readonly string[],\n options: ShellExecOptions | undefined,\n context: Context,\n spawned?: (process: KernelProcess) => void\n): Promise<Result<ShellExecResult, ExecutionError>> {\n const argv = typeof command === 'string' ? [...SHELL, command] : [...command];\n if (argv.length === 0) return err(new ExecutionError('spawn_error', 'No program to run'));\n if (context.abortSignal?.aborted)\n return err(new ExecutionError('aborted', 'The command was aborted'));\n const spill = new Spill(options?.spill);\n const out = sink('stdout', spill, options, context);\n const errors = sink('stderr', spill, options, context);\n let process: KernelProcess;\n try {\n process = await client.spawn(argv, {\n cwd: resolve(cwd, options?.cwd ?? '.'),\n ...(options?.env ? { env: options.env } : {}),\n onStdout: (bytes) => out.push(bytes),\n onStderr: (bytes) => errors.push(bytes),\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return err(new ExecutionError('spawn_error', message));\n }\n spawned?.(process);\n const watch = guard(process, options, context);\n const exitCode = await process.exited.finally(() => watch.release());\n out.end();\n errors.end();\n const spillPath = await spill.write(client);\n const ending = watch.ending;\n if (ending) {\n const error = new ExecutionError(\n ending,\n `The command was ${ending === 'timeout' ? 'timed out' : 'aborted'}`\n );\n if (spillPath) error.spillPath = spillPath;\n return err(error);\n }\n return ok(spillPath ? { exitCode, spillPath } : { exitCode });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
function processGroups(client) {
|
|
2
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3
|
+
return {
|
|
4
|
+
track(owner, process) {
|
|
5
|
+
const set = groups.get(owner) ?? /* @__PURE__ */ new Set();
|
|
6
|
+
groups.set(owner, set);
|
|
7
|
+
set.add(process.pgid);
|
|
8
|
+
process.exited.then(
|
|
9
|
+
() => set.delete(process.pgid),
|
|
10
|
+
() => set.delete(process.pgid)
|
|
11
|
+
);
|
|
12
|
+
},
|
|
13
|
+
of: (owner) => [...groups.get(owner) ?? []],
|
|
14
|
+
async signal(owner, signal = "SIGTERM") {
|
|
15
|
+
const signalled = [];
|
|
16
|
+
for (const pgid of groups.get(owner) ?? []) {
|
|
17
|
+
try {
|
|
18
|
+
await client.kill?.(-pgid, signal);
|
|
19
|
+
signalled.push(pgid);
|
|
20
|
+
} catch {
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
groups.delete(owner);
|
|
24
|
+
return signalled;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
processGroups
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=groups.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/kernel/groups.ts"],
|
|
4
|
+
"sourcesContent": ["import type { KernelClient, KernelProcess } from './client.ts';\n\nexport interface ProcessGroups {\n track(owner: number, process: KernelProcess): void;\n of(owner: number): number[];\n signal(owner: number, signal?: string): Promise<number[]>;\n}\n\nexport function processGroups(client: Pick<KernelClient, 'kill'>): ProcessGroups {\n const groups = new Map<number, Set<number>>();\n return {\n track(owner, process) {\n const set = groups.get(owner) ?? new Set<number>();\n groups.set(owner, set);\n set.add(process.pgid);\n process.exited.then(\n () => set.delete(process.pgid),\n () => set.delete(process.pgid)\n );\n },\n of: (owner) => [...(groups.get(owner) ?? [])],\n async signal(owner, signal = 'SIGTERM') {\n const signalled: number[] = [];\n for (const pgid of groups.get(owner) ?? []) {\n try {\n await client.kill?.(-pgid, signal);\n signalled.push(pgid);\n } catch {}\n }\n groups.delete(owner);\n return signalled;\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "AAQO,SAAS,cAAc,QAAmD;AAC/E,QAAM,SAAS,oBAAI,IAAyB;AAC5C,SAAO;AAAA,IACL,MAAM,OAAO,SAAS;AACpB,YAAM,MAAM,OAAO,IAAI,KAAK,KAAK,oBAAI,IAAY;AACjD,aAAO,IAAI,OAAO,GAAG;AACrB,UAAI,IAAI,QAAQ,IAAI;AACpB,cAAQ,OAAO;AAAA,QACb,MAAM,IAAI,OAAO,QAAQ,IAAI;AAAA,QAC7B,MAAM,IAAI,OAAO,QAAQ,IAAI;AAAA,MAC/B;AAAA,IACF;AAAA,IACA,IAAI,CAAC,UAAU,CAAC,GAAI,OAAO,IAAI,KAAK,KAAK,CAAC,CAAE;AAAA,IAC5C,MAAM,OAAO,OAAO,SAAS,WAAW;AACtC,YAAM,YAAsB,CAAC;AAC7B,iBAAW,QAAQ,OAAO,IAAI,KAAK,KAAK,CAAC,GAAG;AAC1C,YAAI;AACF,gBAAM,OAAO,OAAO,CAAC,MAAM,MAAM;AACjC,oBAAU,KAAK,IAAI;AAAA,QACrB,QAAQ;AAAA,QAAC;AAAA,MACX;AACA,aAAO,OAAO,KAAK;AACnB,aAAO;AAAA,IACT;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/kernel/watch.js
CHANGED
|
@@ -41,8 +41,24 @@ function differences(before, after) {
|
|
|
41
41
|
for (const path of before.keys()) if (!after.has(path)) moved(path);
|
|
42
42
|
return [...changed];
|
|
43
43
|
}
|
|
44
|
+
const RESCAN_MS = 2e3;
|
|
45
|
+
function baseOf(path, target) {
|
|
46
|
+
if (inside(path, target.path)) return target.path;
|
|
47
|
+
return inside(target.path, path) ? path : void 0;
|
|
48
|
+
}
|
|
49
|
+
async function absorbPaths(fs, current, targets, paths) {
|
|
50
|
+
for (const path of paths)
|
|
51
|
+
for (const target of targets) {
|
|
52
|
+
const base = baseOf(path, target);
|
|
53
|
+
if (base === void 0) continue;
|
|
54
|
+
for (const key of [...current.keys()]) if (inside(base, key)) current.delete(key);
|
|
55
|
+
await scan(fs, base, target, base === target.path || target.recursive === true, current);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
44
58
|
async function pollWatch(fs, cwd, targets, onChange, options = {}) {
|
|
45
59
|
let current = await snapshot(fs, cwd, targets);
|
|
60
|
+
const resolved = targets.map((target) => ({ ...target, path: resolve(cwd, target.path) }));
|
|
61
|
+
const refresh = (paths) => absorbPaths(fs, current, resolved, paths);
|
|
46
62
|
let open = true;
|
|
47
63
|
let running = Promise.resolve();
|
|
48
64
|
const tick = async () => {
|
|
@@ -56,6 +72,9 @@ async function pollWatch(fs, cwd, targets, onChange, options = {}) {
|
|
|
56
72
|
}, options.intervalMs ?? 100);
|
|
57
73
|
return {
|
|
58
74
|
mode: "polling",
|
|
75
|
+
absorb(paths) {
|
|
76
|
+
running = running.then(() => refresh(paths));
|
|
77
|
+
},
|
|
59
78
|
async close(_context) {
|
|
60
79
|
open = false;
|
|
61
80
|
clearInterval(timer);
|
|
@@ -63,8 +82,75 @@ async function pollWatch(fs, cwd, targets, onChange, options = {}) {
|
|
|
63
82
|
}
|
|
64
83
|
};
|
|
65
84
|
}
|
|
85
|
+
function hidden(base, path, target) {
|
|
86
|
+
if (!target.exclude || path === base) return false;
|
|
87
|
+
const below = path.slice(base === "/" ? 1 : base.length + 1);
|
|
88
|
+
return below.split("/").some((name) => excluded(name, target));
|
|
89
|
+
}
|
|
90
|
+
function inside(base, path) {
|
|
91
|
+
return path === base || path.startsWith(base === "/" ? "/" : `${base}/`);
|
|
92
|
+
}
|
|
93
|
+
const FALLBACK = /* @__PURE__ */ new Set(["ENOSYS", "ENOENT", "ENOTDIR"]);
|
|
94
|
+
function mapChange(targets, path) {
|
|
95
|
+
const out = [];
|
|
96
|
+
for (const target of targets) {
|
|
97
|
+
if (inside(target.path, path)) {
|
|
98
|
+
if (!hidden(target.path, path, target)) out.push(path);
|
|
99
|
+
} else if (inside(path, target.path)) out.push(target.path);
|
|
100
|
+
else if (target.real !== target.path && inside(target.real, path)) {
|
|
101
|
+
const mapped = `${target.path}${path.slice(target.real.length)}`;
|
|
102
|
+
if (!hidden(target.path, mapped, target)) out.push(mapped);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
async function nativeWatch(fs, cwd, targets, onChange) {
|
|
108
|
+
const watch = fs.watch?.bind(fs);
|
|
109
|
+
if (!watch) return void 0;
|
|
110
|
+
const resolved = [];
|
|
111
|
+
for (const target of targets) {
|
|
112
|
+
const path = resolve(cwd, target.path);
|
|
113
|
+
if (!await fs.exists(path).catch(() => false)) return void 0;
|
|
114
|
+
resolved.push({ ...target, path, real: await fs.realpath(path).catch(() => path) });
|
|
115
|
+
}
|
|
116
|
+
let open = true;
|
|
117
|
+
const handles = [];
|
|
118
|
+
const closeAll = async () => {
|
|
119
|
+
for (const handle of handles.splice(0)) await handle.close();
|
|
120
|
+
};
|
|
121
|
+
const report = (change) => {
|
|
122
|
+
if (!open) return;
|
|
123
|
+
if (!("paths" in change)) {
|
|
124
|
+
onChange({ overflow: true });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const paths = [...new Set(change.paths.flatMap((path) => mapChange(resolved, path)))];
|
|
128
|
+
if (paths.length) onChange({ paths });
|
|
129
|
+
};
|
|
130
|
+
try {
|
|
131
|
+
for (const target of resolved) {
|
|
132
|
+
const recursive = { recursive: target.recursive === true };
|
|
133
|
+
handles.push(await watch([target.path], recursive, report));
|
|
134
|
+
if (target.real !== target.path) handles.push(await watch([target.real], recursive, report));
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
await closeAll();
|
|
138
|
+
if (FALLBACK.has(error.code ?? "")) return void 0;
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
mode: "native",
|
|
143
|
+
async close(_context) {
|
|
144
|
+
open = false;
|
|
145
|
+
await closeAll();
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
66
149
|
export {
|
|
150
|
+
RESCAN_MS,
|
|
67
151
|
differences,
|
|
152
|
+
mapChange,
|
|
153
|
+
nativeWatch,
|
|
68
154
|
pollWatch,
|
|
69
155
|
snapshot
|
|
70
156
|
};
|
package/dist/kernel/watch.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/kernel/watch.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { FileWatcher, WatchChange, WatchTarget } from '@earendil-works/pi-durable/env';\nimport type { KernelFs } from './client.ts';\nimport { dirname, resolve } from './paths.ts';\n\ntype Snapshot = Map<string, string>;\n\nfunction excluded(name: string, target: WatchTarget): boolean {\n const rule = target.exclude;\n if (!rule) return false;\n return (rule.hidden === true && name.startsWith('.')) || (rule.names?.includes(name) ?? false);\n}\n\nasync function record(fs: KernelFs, path: string, into: Snapshot): Promise<boolean> {\n const stat = await fs.stat(path).catch(() => undefined);\n if (!stat) return false;\n into.set(\n path,\n `${stat.isDirectory ? 'd' : 'f'}:${stat.ino}:${stat.size}:${new Date(stat.mtime).getTime()}`\n );\n return stat.isDirectory;\n}\n\nasync function scan(\n fs: KernelFs,\n path: string,\n target: WatchTarget,\n deep: boolean,\n into: Snapshot\n) {\n const directory = await record(fs, path, into);\n if (!directory || !deep) return;\n const names = await fs.readdir(path).catch(() => [] as string[]);\n for (const name of names) {\n if (excluded(name, target)) continue;\n const child = `${path === '/' ? '' : path}/${name}`;\n await scan(fs, child, target, target.recursive === true, into);\n }\n}\n\nexport async function snapshot(\n fs: KernelFs,\n cwd: string,\n targets: readonly WatchTarget[]\n): Promise<Snapshot> {\n const all: Snapshot = new Map();\n for (const target of targets) await scan(fs, resolve(cwd, target.path), target, true, all);\n return all;\n}\n\nexport function differences(before: Snapshot, after: Snapshot): string[] {\n const changed = new Set<string>();\n const moved = (path: string) => {\n changed.add(path);\n changed.add(dirname(path));\n };\n for (const [path, value] of after) {\n if (!before.has(path)) moved(path);\n else if (before.get(path) !== value) changed.add(path);\n }\n for (const path of before.keys()) if (!after.has(path)) moved(path);\n return [...changed];\n}\n\nexport interface PollOptions {\n intervalMs?: number;\n}\n\nexport async function pollWatch(\n fs: KernelFs,\n cwd: string,\n targets: readonly WatchTarget[],\n onChange: (change: WatchChange) => void,\n options: PollOptions = {}\n): Promise<
|
|
5
|
-
"mappings": "AAGA,SAAS,SAAS,eAAe;AAIjC,SAAS,SAAS,MAAc,QAA8B;AAC5D,QAAM,OAAO,OAAO;AACpB,MAAI,CAAC,KAAM,QAAO;AAClB,SAAQ,KAAK,WAAW,QAAQ,KAAK,WAAW,GAAG,MAAO,KAAK,OAAO,SAAS,IAAI,KAAK;AAC1F;AAEA,eAAe,OAAO,IAAc,MAAc,MAAkC;AAClF,QAAM,OAAO,MAAM,GAAG,KAAK,IAAI,EAAE,MAAM,MAAM,MAAS;AACtD,MAAI,CAAC,KAAM,QAAO;AAClB,OAAK;AAAA,IACH;AAAA,IACA,GAAG,KAAK,cAAc,MAAM,GAAG,IAAI,KAAK,GAAG,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,KAAK,EAAE,QAAQ,CAAC;AAAA,EAC5F;AACA,SAAO,KAAK;AACd;AAEA,eAAe,KACb,IACA,MACA,QACA,MACA,MACA;AACA,QAAM,YAAY,MAAM,OAAO,IAAI,MAAM,IAAI;AAC7C,MAAI,CAAC,aAAa,CAAC,KAAM;AACzB,QAAM,QAAQ,MAAM,GAAG,QAAQ,IAAI,EAAE,MAAM,MAAM,CAAC,CAAa;AAC/D,aAAW,QAAQ,OAAO;AACxB,QAAI,SAAS,MAAM,MAAM,EAAG;AAC5B,UAAM,QAAQ,GAAG,SAAS,MAAM,KAAK,IAAI,IAAI,IAAI;AACjD,UAAM,KAAK,IAAI,OAAO,QAAQ,OAAO,cAAc,MAAM,IAAI;AAAA,EAC/D;AACF;AAEA,eAAsB,SACpB,IACA,KACA,SACmB;AACnB,QAAM,MAAgB,oBAAI,IAAI;AAC9B,aAAW,UAAU,QAAS,OAAM,KAAK,IAAI,QAAQ,KAAK,OAAO,IAAI,GAAG,QAAQ,MAAM,GAAG;AACzF,SAAO;AACT;AAEO,SAAS,YAAY,QAAkB,OAA2B;AACvE,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,QAAQ,CAAC,SAAiB;AAC9B,YAAQ,IAAI,IAAI;AAChB,YAAQ,IAAI,QAAQ,IAAI,CAAC;AAAA,EAC3B;AACA,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO;AACjC,QAAI,CAAC,OAAO,IAAI,IAAI,EAAG,OAAM,IAAI;AAAA,aACxB,OAAO,IAAI,IAAI,MAAM,MAAO,SAAQ,IAAI,IAAI;AAAA,EACvD;AACA,aAAW,QAAQ,OAAO,KAAK,EAAG,KAAI,CAAC,MAAM,IAAI,IAAI,EAAG,OAAM,IAAI;AAClE,SAAO,CAAC,GAAG,OAAO;AACpB;
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { FileWatcher, WatchChange, WatchTarget } from '@earendil-works/pi-durable/env';\nimport type { KernelFs } from './client.ts';\nimport { dirname, resolve } from './paths.ts';\n\ntype Snapshot = Map<string, string>;\n\nfunction excluded(name: string, target: WatchTarget): boolean {\n const rule = target.exclude;\n if (!rule) return false;\n return (rule.hidden === true && name.startsWith('.')) || (rule.names?.includes(name) ?? false);\n}\n\nasync function record(fs: KernelFs, path: string, into: Snapshot): Promise<boolean> {\n const stat = await fs.stat(path).catch(() => undefined);\n if (!stat) return false;\n into.set(\n path,\n `${stat.isDirectory ? 'd' : 'f'}:${stat.ino}:${stat.size}:${new Date(stat.mtime).getTime()}`\n );\n return stat.isDirectory;\n}\n\nasync function scan(\n fs: KernelFs,\n path: string,\n target: WatchTarget,\n deep: boolean,\n into: Snapshot\n) {\n const directory = await record(fs, path, into);\n if (!directory || !deep) return;\n const names = await fs.readdir(path).catch(() => [] as string[]);\n for (const name of names) {\n if (excluded(name, target)) continue;\n const child = `${path === '/' ? '' : path}/${name}`;\n await scan(fs, child, target, target.recursive === true, into);\n }\n}\n\nexport async function snapshot(\n fs: KernelFs,\n cwd: string,\n targets: readonly WatchTarget[]\n): Promise<Snapshot> {\n const all: Snapshot = new Map();\n for (const target of targets) await scan(fs, resolve(cwd, target.path), target, true, all);\n return all;\n}\n\nexport function differences(before: Snapshot, after: Snapshot): string[] {\n const changed = new Set<string>();\n const moved = (path: string) => {\n changed.add(path);\n changed.add(dirname(path));\n };\n for (const [path, value] of after) {\n if (!before.has(path)) moved(path);\n else if (before.get(path) !== value) changed.add(path);\n }\n for (const path of before.keys()) if (!after.has(path)) moved(path);\n return [...changed];\n}\n\nexport interface PollOptions {\n intervalMs?: number;\n rescanMs?: number;\n mode?: 'native' | 'polling';\n}\n\nexport const RESCAN_MS = 2000;\n\nfunction baseOf(path: string, target: WatchTarget): string | undefined {\n if (inside(path, target.path)) return target.path;\n return inside(target.path, path) ? path : undefined;\n}\n\nasync function absorbPaths(\n fs: KernelFs,\n current: Snapshot,\n targets: readonly WatchTarget[],\n paths: readonly string[]\n): Promise<void> {\n for (const path of paths)\n for (const target of targets) {\n const base = baseOf(path, target);\n if (base === undefined) continue;\n for (const key of [...current.keys()]) if (inside(base, key)) current.delete(key);\n await scan(fs, base, target, base === target.path || target.recursive === true, current);\n }\n}\n\nexport type PolledWatcher = FileWatcher & { absorb(paths: readonly string[]): void };\n\nexport async function pollWatch(\n fs: KernelFs,\n cwd: string,\n targets: readonly WatchTarget[],\n onChange: (change: WatchChange) => void,\n options: PollOptions = {}\n): Promise<PolledWatcher> {\n let current = await snapshot(fs, cwd, targets);\n const resolved = targets.map((target) => ({ ...target, path: resolve(cwd, target.path) }));\n const refresh = (paths: readonly string[]) => absorbPaths(fs, current, resolved, paths);\n let open = true;\n let running = Promise.resolve();\n const tick = async () => {\n const next = await snapshot(fs, cwd, targets);\n const paths = differences(current, next);\n current = next;\n if (open && paths.length > 0) onChange({ paths });\n };\n const timer = setInterval(() => {\n running = running.then(tick);\n }, options.intervalMs ?? 100);\n return {\n mode: 'polling',\n absorb(paths) {\n running = running.then(() => refresh(paths));\n },\n async close(_context: Context) {\n open = false;\n clearInterval(timer);\n await running;\n },\n };\n}\n\nfunction hidden(base: string, path: string, target: WatchTarget): boolean {\n if (!target.exclude || path === base) return false;\n const below = path.slice(base === '/' ? 1 : base.length + 1);\n return below.split('/').some((name) => excluded(name, target));\n}\n\nfunction inside(base: string, path: string): boolean {\n return path === base || path.startsWith(base === '/' ? '/' : `${base}/`);\n}\n\nconst FALLBACK = new Set(['ENOSYS', 'ENOENT', 'ENOTDIR']);\n\ntype Watched = WatchTarget & { real: string };\n\nexport function mapChange(targets: readonly Watched[], path: string): string[] {\n const out: string[] = [];\n for (const target of targets) {\n if (inside(target.path, path)) {\n if (!hidden(target.path, path, target)) out.push(path);\n } else if (inside(path, target.path)) out.push(target.path);\n else if (target.real !== target.path && inside(target.real, path)) {\n const mapped = `${target.path}${path.slice(target.real.length)}`;\n if (!hidden(target.path, mapped, target)) out.push(mapped);\n }\n }\n return out;\n}\n\nexport async function nativeWatch(\n fs: KernelFs,\n cwd: string,\n targets: readonly WatchTarget[],\n onChange: (change: WatchChange) => void\n): Promise<FileWatcher | undefined> {\n const watch = fs.watch?.bind(fs);\n if (!watch) return undefined;\n const resolved: Watched[] = [];\n for (const target of targets) {\n const path = resolve(cwd, target.path);\n if (!(await fs.exists(path).catch(() => false))) return undefined;\n resolved.push({ ...target, path, real: await fs.realpath(path).catch(() => path) });\n }\n let open = true;\n const handles: { close(): unknown }[] = [];\n const closeAll = async () => {\n for (const handle of handles.splice(0)) await handle.close();\n };\n const report = (change: { paths: string[] } | { overflow: true }) => {\n if (!open) return;\n if (!('paths' in change)) {\n onChange({ overflow: true });\n return;\n }\n const paths = [...new Set(change.paths.flatMap((path) => mapChange(resolved, path)))];\n if (paths.length) onChange({ paths });\n };\n try {\n for (const target of resolved) {\n const recursive = { recursive: target.recursive === true };\n handles.push(await watch([target.path], recursive, report));\n if (target.real !== target.path) handles.push(await watch([target.real], recursive, report));\n }\n } catch (error) {\n await closeAll();\n if (FALLBACK.has((error as { code?: string }).code ?? '')) return undefined;\n throw error;\n }\n return {\n mode: 'native',\n async close(_context: Context) {\n open = false;\n await closeAll();\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|