@codeworksh/cli 0.0.1-dev.20260918042316 → 0.0.1-dev.20260922135856
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 +51 -0
- package/add-D7X5BuCv.mjs +81 -0
- package/add-D7X5BuCv.mjs.map +1 -0
- package/{catalog-Cv1zoZ5S.mjs → catalog-DL8yWdvI.mjs} +2 -2
- package/{catalog-Cv1zoZ5S.mjs.map → catalog-DL8yWdvI.mjs.map} +1 -1
- package/check-M9CqztTk.mjs +47 -0
- package/check-M9CqztTk.mjs.map +1 -0
- package/commonjs-CiyyOsS6.mjs +160 -0
- package/commonjs-CiyyOsS6.mjs.map +1 -0
- package/definitions-ClJIBYEm.mjs +3348 -0
- package/definitions-ClJIBYEm.mjs.map +1 -0
- package/entries-DBdy6ahj.mjs +48 -0
- package/entries-DBdy6ahj.mjs.map +1 -0
- package/{error-zJGKAF4a.mjs → error-DTXBm1nO.mjs} +2348 -1841
- package/error-DTXBm1nO.mjs.map +1 -0
- package/{generate-DdscbX3w.mjs → generate-C5NElYXe.mjs} +3 -3
- package/{generate-DdscbX3w.mjs.map → generate-C5NElYXe.mjs.map} +1 -1
- package/{harness-BBTdWdhx.mjs → harness-DQxbts5f.mjs} +2 -2
- package/{harness-BBTdWdhx.mjs.map → harness-DQxbts5f.mjs.map} +1 -1
- package/index.mjs +16 -6
- package/index.mjs.map +1 -1
- package/install-CJEnZ9dx.mjs +53 -0
- package/install-CJEnZ9dx.mjs.map +1 -0
- package/lib-5d-Jmuz2.mjs +16078 -0
- package/lib-5d-Jmuz2.mjs.map +1 -0
- package/lib-CVW4jqJ3.mjs +37659 -0
- package/lib-CVW4jqJ3.mjs.map +1 -0
- package/lib-HXZBVLKt.mjs +846 -0
- package/lib-HXZBVLKt.mjs.map +1 -0
- package/lib-vrwKjvzk.mjs +10868 -0
- package/lib-vrwKjvzk.mjs.map +1 -0
- package/link-D8V3E5iZ.mjs +40 -0
- package/link-D8V3E5iZ.mjs.map +1 -0
- package/{list-C6BrwuV-.mjs → list-BMUDpLRf.mjs} +4 -4
- package/{list-C6BrwuV-.mjs.map → list-BMUDpLRf.mjs.map} +1 -1
- package/list-DS9BJ3dD.mjs +76 -0
- package/list-DS9BJ3dD.mjs.map +1 -0
- package/p-map-LZ99xLhZ.mjs +116 -0
- package/p-map-LZ99xLhZ.mjs.map +1 -0
- package/package.json +11 -8
- package/{providers-fO-GJuXn.mjs → providers-DT5BfXJV.mjs} +4 -4
- package/{providers-fO-GJuXn.mjs.map → providers-DT5BfXJV.mjs.map} +1 -1
- package/reload-2GZ_8UBW.mjs +26 -0
- package/reload-2GZ_8UBW.mjs.map +1 -0
- package/remove-t8FHVGC0.mjs +36 -0
- package/remove-t8FHVGC0.mjs.map +1 -0
- package/{run-CgPOJ-wJ.mjs → run-CP7aAfkf.mjs} +11 -8
- package/run-CP7aAfkf.mjs.map +1 -0
- package/{runtime-DuL-GGrx.mjs → runtime-CDSh4qOj.mjs} +108 -1
- package/runtime-CDSh4qOj.mjs.map +1 -0
- package/{serve-Du6luqoi.mjs → serve-DewC8gvV.mjs} +19 -6
- package/serve-DewC8gvV.mjs.map +1 -0
- package/settings-Bn2pmQXX.mjs +215 -0
- package/settings-Bn2pmQXX.mjs.map +1 -0
- package/type-defs-xvRvQI4T.mjs +538 -0
- package/type-defs-xvRvQI4T.mjs.map +1 -0
- package/update-CLfU-oh7.mjs +44 -0
- package/update-CLfU-oh7.mjs.map +1 -0
- package/valid-BXe5q7ao.mjs +354 -0
- package/valid-BXe5q7ao.mjs.map +1 -0
- package/error-zJGKAF4a.mjs.map +0 -1
- package/run-CgPOJ-wJ.mjs.map +0 -1
- package/runtime-DuL-GGrx.mjs.map +0 -1
- package/serve-Du6luqoi.mjs.map +0 -1
|
@@ -81,6 +81,113 @@ const Cmd = make("codework", {
|
|
|
81
81
|
description: "Choose an available local port"
|
|
82
82
|
}]
|
|
83
83
|
}),
|
|
84
|
+
make("session", {
|
|
85
|
+
description: "Manage sessions",
|
|
86
|
+
commands: [make("link", {
|
|
87
|
+
description: "Anchor a session to a host directory",
|
|
88
|
+
params: {
|
|
89
|
+
session: Argument.String("session-id").pipe(Argument.withDescription("The session to link")),
|
|
90
|
+
path: Argument.String("path").pipe(Argument.withDescription("Host directory to anchor to — typically the directory holding your .codework configuration; defaults to the shell's"), Argument.optional),
|
|
91
|
+
unlink: Flag.Boolean("unlink").pipe(Flag.withDescription("Clear the anchor, leaving the session with no host directory"), Flag.withDefault(false)),
|
|
92
|
+
server: Flag.String("server").pipe(Flag.withDescription("Base URL of the running codework server"), Flag.withDefault("http://127.0.0.1:7433"))
|
|
93
|
+
},
|
|
94
|
+
examples: [
|
|
95
|
+
{
|
|
96
|
+
command: "codework session link ses_01h9",
|
|
97
|
+
description: "Link it to the directory you are standing in"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
command: "codework session link ses_01h9 ~/work/api",
|
|
101
|
+
description: "Link it to a project elsewhere on this machine"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
command: "codework session link ses_01h9 --unlink",
|
|
105
|
+
description: "Clear the anchor"
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
})]
|
|
109
|
+
}),
|
|
110
|
+
make("plugin", {
|
|
111
|
+
description: "Manage plugins",
|
|
112
|
+
commands: [
|
|
113
|
+
make("add", {
|
|
114
|
+
description: "Install a plugin and add it to a settings file",
|
|
115
|
+
params: {
|
|
116
|
+
package: Argument.String("package").pipe(Argument.withDescription("Package spec, path, or file: URL")),
|
|
117
|
+
global: Flag.Boolean("global").pipe(Flag.withAlias("g"), Flag.withDescription("Write the user-wide settings file instead of this project's"), Flag.withDefault(false)),
|
|
118
|
+
session: Flag.String("session").pipe(Flag.withDescription("Write the project a session is linked to, rather than this directory's"), Flag.optional)
|
|
119
|
+
},
|
|
120
|
+
examples: [
|
|
121
|
+
{
|
|
122
|
+
command: "codework plugin add @acme/codework-tool-proc@1.2.0",
|
|
123
|
+
description: "Install a published plugin into this project's settings"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
command: "codework plugin add @acme/codework-tool-proc --session ses_01h9",
|
|
127
|
+
description: "Write the project that session is linked to, from anywhere"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
command: "codework plugin add @acme/codework-tool-proc -g",
|
|
131
|
+
description: "Install it for every project this user runs"
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
}),
|
|
135
|
+
make("install", {
|
|
136
|
+
description: "Install every plugin the settings files name",
|
|
137
|
+
params: {},
|
|
138
|
+
examples: [{
|
|
139
|
+
command: "codework plugin install",
|
|
140
|
+
description: "Materialise what a fresh checkout already declares"
|
|
141
|
+
}]
|
|
142
|
+
}),
|
|
143
|
+
make("list", {
|
|
144
|
+
description: "List configured plugins and what each one resolved to",
|
|
145
|
+
params: { verbose: Flag.Boolean("verbose").pipe(Flag.withDescription("Show the message behind a failure, not only its reason"), Flag.withDefault(false)) },
|
|
146
|
+
examples: [{
|
|
147
|
+
command: "codework plugin list",
|
|
148
|
+
description: "Show every configured plugin"
|
|
149
|
+
}]
|
|
150
|
+
}),
|
|
151
|
+
make("check", {
|
|
152
|
+
description: "Report which configured plugins have a newer revision available",
|
|
153
|
+
params: { refresh: Flag.Boolean("refresh").pipe(Flag.withDescription("Ask the remote again instead of reusing a recent answer"), Flag.withDefault(false)) },
|
|
154
|
+
examples: [{
|
|
155
|
+
command: "codework plugin check",
|
|
156
|
+
description: "Ask the registry what has moved"
|
|
157
|
+
}, {
|
|
158
|
+
command: "codework plugin check --refresh",
|
|
159
|
+
description: "Ignore the cached answer, after pushing a plugin"
|
|
160
|
+
}]
|
|
161
|
+
}),
|
|
162
|
+
make("update", {
|
|
163
|
+
description: "Fetch a newer revision of every plugin that has one",
|
|
164
|
+
params: {},
|
|
165
|
+
examples: [{
|
|
166
|
+
command: "codework plugin update",
|
|
167
|
+
description: "Take whatever `check` found"
|
|
168
|
+
}]
|
|
169
|
+
}),
|
|
170
|
+
make("reload", {
|
|
171
|
+
description: "Re-import configured plugins in a running server",
|
|
172
|
+
params: { server: Flag.String("server").pipe(Flag.withDescription("Base URL of the running codework server"), Flag.withDefault("http://127.0.0.1:7433")) },
|
|
173
|
+
examples: [{
|
|
174
|
+
command: "codework plugin reload",
|
|
175
|
+
description: "Pick up edits to a local plugin without restarting"
|
|
176
|
+
}]
|
|
177
|
+
}),
|
|
178
|
+
make("remove", {
|
|
179
|
+
description: "Remove a plugin from a settings file",
|
|
180
|
+
params: {
|
|
181
|
+
package: Argument.String("package").pipe(Argument.withDescription("Configured package spec, path, or plugin ID")),
|
|
182
|
+
global: Flag.Boolean("global").pipe(Flag.withAlias("g"), Flag.withDescription("Edit the user-wide settings file instead of this project's"), Flag.withDefault(false))
|
|
183
|
+
},
|
|
184
|
+
examples: [{
|
|
185
|
+
command: "codework plugin remove @acme/codework-tool-proc",
|
|
186
|
+
description: "Drop a plugin and any configuration written against it"
|
|
187
|
+
}]
|
|
188
|
+
})
|
|
189
|
+
]
|
|
190
|
+
}),
|
|
84
191
|
make("models", {
|
|
85
192
|
description: "List or generate the model catalog",
|
|
86
193
|
params: { provider: Flag.String("provider").pipe(Flag.withDescription("Model catalog provider ID"), Flag.optional) },
|
|
@@ -160,4 +267,4 @@ function provide(node, handlers) {
|
|
|
160
267
|
//#endregion
|
|
161
268
|
export { Cmd as i, handlers as n, run as r, handler as t };
|
|
162
269
|
|
|
163
|
-
//# sourceMappingURL=runtime-
|
|
270
|
+
//# sourceMappingURL=runtime-CDSh4qOj.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-CDSh4qOj.mjs","names":["Spec.make"],"sources":["../../src/framework/spec.ts","../../src/cli/cmd/cmd.ts","../../src/framework/runtime.ts"],"sourcesContent":["import { Command } from \"effect/unstable/cli\";\n\n/**\n * A declarative command tree. Specs carry only the shape of a command -- its\n * name, parameters, description, and children -- so the whole CLI surface can\n * be read in one file. Handlers are attached separately by {@link Runtime}, which\n * keeps their imports (and the harness they pull in) off the startup path.\n */\n\nexport interface Example {\n\treadonly command: string;\n\treadonly description: string;\n}\n\ninterface Options<\n\tConfig extends Command.Command.Config,\n\tShared extends Command.Command.FlagConfig,\n\tCommands extends ReadonlyArray<Any>,\n> {\n\treadonly description?: string;\n\treadonly params?: Config;\n\t/** Flags declared here are also parsed by every descendant command. */\n\treadonly shared?: Shared;\n\treadonly examples?: ReadonlyArray<Example>;\n\treadonly commands?: Commands;\n}\n\nexport interface Node<\n\tName extends string,\n\t// The tree is heterogeneous, so a node has to accept any command shape here.\n\t// Concrete types are recovered per-command through `Runtime.Input`.\n\t// biome-ignore lint/suspicious/noExplicitAny: heterogeneous command tree\n\tSpec extends Command.Command<Name, any, any, any, any>,\n\tCommands extends Children,\n> {\n\treadonly name: Name;\n\treadonly spec: Spec;\n\treadonly commands: Commands;\n}\n\n// The command name is invariant on `Command`, so the erased node type has to\n// admit any name for concrete nodes to be assignable to it.\n// biome-ignore lint/suspicious/noExplicitAny: heterogeneous command tree\nexport type Any = Node<string, Command.Command<any, any, any, any, any>, Children>;\nexport type Children = Readonly<Record<string, Any>>;\n\ntype ChildrenOf<Commands extends ReadonlyArray<Any>> = {\n\treadonly [Node in Commands[number] as Node[\"name\"]]: Node;\n};\n\nexport function make<\n\tconst Name extends string,\n\tconst Config extends Command.Command.Config = {},\n\tconst Shared extends Command.Command.FlagConfig = {},\n\tconst Commands extends ReadonlyArray<Any> = [],\n>(name: Name, options: Options<Config, Shared, Commands> = {}) {\n\tlet spec = Command.make(name, options.params ?? ({} as Config)).pipe(\n\t\tCommand.withSharedFlags(options.shared ?? ({} as Shared)),\n\t);\n\tif (options.description !== undefined) spec = spec.pipe(Command.withDescription(options.description));\n\tif (options.examples !== undefined) spec = spec.pipe(Command.withExamples(options.examples));\n\treturn {\n\t\tname,\n\t\tspec,\n\t\tcommands: Object.fromEntries(\n\t\t\t(options.commands ?? []).map((command) => [command.name, command]),\n\t\t) as ChildrenOf<Commands>,\n\t};\n}\n\nexport * as Spec from \"./spec.ts\";\n","import { Schema } from \"effect\";\nimport { Model } from \"@codeworksh/aikit\";\nimport { Argument, Flag } from \"effect/unstable/cli\";\nimport { Spec } from \"../../framework/spec.ts\";\n\n/**\n * The full command surface of the `codework` CLI. Declarations only -- each\n * command's behaviour lives in `./handlers`, wired up in `src/index.ts`.\n */\n\nexport const thinkingLevels = Object.values(Model.ThinkingLevelEnum);\n\nexport const Cmd = Spec.make(\"codework\", {\n\tdescription: \"CodeWork Command Line Interface\",\n\tshared: {\n\t\tuserConfigDir: Flag.String(\"user-config-dir\").pipe(\n\t\t\tFlag.withDescription(\"Directory containing user config overrides (e.g. settings.json)\"),\n\t\t\tFlag.optional,\n\t\t),\n\t\thome: Flag.String(\"home\").pipe(\n\t\t\tFlag.withDescription(\"CodeWork data directory (default: ~/.codework)\"),\n\t\t\tFlag.optional,\n\t\t),\n\t\tdatabase: Flag.String(\"database\").pipe(Flag.withDescription(\"SQLite database path or :memory:\"), Flag.optional),\n\t},\n\tcommands: [\n\t\tSpec.make(\"run\", {\n\t\t\tdescription: \"Start or continue an agent session\",\n\t\t\tparams: {\n\t\t\t\tserver: Flag.String(\"server\").pipe(\n\t\t\t\t\tFlag.withDescription(\"RPC server URL (ws://host:port/rpc)\"),\n\t\t\t\t\tFlag.optional,\n\t\t\t\t),\n\t\t\t\tprompt: Argument.String(\"prompt\").pipe(Argument.withDescription(\"Prompt for the agent\")),\n\t\t\t\tsession: Flag.String(\"session\").pipe(\n\t\t\t\t\tFlag.withAlias(\"s\"),\n\t\t\t\t\tFlag.withDescription(\"Continue an existing session\"),\n\t\t\t\t\tFlag.optional,\n\t\t\t\t),\n\t\t\t\tcwd: Flag.String(\"cwd\").pipe(\n\t\t\t\t\tFlag.withAlias(\"C\"),\n\t\t\t\t\tFlag.withDescription(\"Working directory for a new session\"),\n\t\t\t\t\tFlag.optional,\n\t\t\t\t),\n\t\t\t\tsandboxDriver: Flag.String(\"sandbox-driver\").pipe(\n\t\t\t\t\tFlag.withDescription(\"Sandbox driver for a new session (default: local)\"),\n\t\t\t\t\tFlag.optional,\n\t\t\t\t),\n\t\t\t\tsandboxId: Flag.String(\"sandbox-id\").pipe(\n\t\t\t\t\tFlag.withDescription(\"Reuse an existing sandbox instance\"),\n\t\t\t\t\tFlag.optional,\n\t\t\t\t),\n\t\t\t\tsandboxProviderId: Flag.String(\"sandbox-provider-id\").pipe(\n\t\t\t\t\tFlag.withDescription(\"Provider ID of an existing remote sandbox\"),\n\t\t\t\t\tFlag.optional,\n\t\t\t\t),\n\t\t\t\tprovider: Flag.String(\"provider\").pipe(Flag.withDescription(\"Model catalog provider ID\"), Flag.optional),\n\t\t\t\tmodel: Flag.String(\"model\").pipe(Flag.withDescription(\"Model ID\"), Flag.optional),\n\t\t\t\tthinking: Flag.Literals(\"thinking\", thinkingLevels).pipe(\n\t\t\t\t\tFlag.withDescription(\"Reasoning effort the model uses before answering\"),\n\t\t\t\t\tFlag.optional,\n\t\t\t\t),\n\t\t\t},\n\t\t\texamples: [\n\t\t\t\t{ command: 'codework run \"Inspect the failing tests\"', description: \"Create a session\" },\n\t\t\t\t{\n\t\t\t\t\tcommand: 'codework run --provider openai --model gpt-5.5 --thinking high \"Inspect the failing tests\"',\n\t\t\t\t\tdescription: \"Create a session with an explicit model\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tcommand: 'codework run --sandbox-driver daytona \"Inspect the repository\"',\n\t\t\t\t\tdescription: \"Create a Daytona sandbox and session\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tcommand: 'codework run --sandbox-driver daytona --sandbox-provider-id <id> \"Inspect the repository\"',\n\t\t\t\t\tdescription: \"Use an existing remote sandbox\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tcommand: 'codework run --session <id> --provider openai --model gpt-5.6-luna \"Now fix them\"',\n\t\t\t\t\tdescription: \"Continue a session with explicit model bindings\",\n\t\t\t\t},\n\t\t\t],\n\t\t}),\n\t\tSpec.make(\"serve\", {\n\t\t\tdescription: \"Start the CodeWork RPC server\",\n\t\t\tparams: {\n\t\t\t\thost: Flag.String(\"host\").pipe(\n\t\t\t\t\tFlag.withDescription(\"Host interface to bind\"),\n\t\t\t\t\tFlag.withDefault(\"127.0.0.1\"),\n\t\t\t\t),\n\t\t\t\tport: Flag.Int(\"port\").pipe(\n\t\t\t\t\tFlag.withSchema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 65535 }))),\n\t\t\t\t\tFlag.withDescription(\"Port to listen on (0 for an ephemeral port)\"),\n\t\t\t\t\tFlag.withDefault(7433),\n\t\t\t\t),\n\t\t\t},\n\t\t\texamples: [\n\t\t\t\t{ command: \"codework serve\", description: \"Start the RPC server on 127.0.0.1:7433\" },\n\t\t\t\t{ command: \"codework serve --port 0\", description: \"Choose an available local port\" },\n\t\t\t],\n\t\t}),\n\t\tSpec.make(\"session\", {\n\t\t\tdescription: \"Manage sessions\",\n\t\t\tcommands: [\n\t\t\t\tSpec.make(\"link\", {\n\t\t\t\t\tdescription: \"Anchor a session to a host directory\",\n\t\t\t\t\tparams: {\n\t\t\t\t\t\tsession: Argument.String(\"session-id\").pipe(Argument.withDescription(\"The session to link\")),\n\t\t\t\t\t\tpath: Argument.String(\"path\").pipe(\n\t\t\t\t\t\t\t// The anchor is not a configuration mechanism, but `.codework` is the landmark\n\t\t\t\t\t\t\t// a person recognises, so the help names it to make the directory concrete.\n\t\t\t\t\t\t\tArgument.withDescription(\n\t\t\t\t\t\t\t\t\"Host directory to anchor to — typically the directory holding your .codework configuration; defaults to the shell's\",\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tArgument.optional,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tunlink: Flag.Boolean(\"unlink\").pipe(\n\t\t\t\t\t\t\tFlag.withDescription(\"Clear the anchor, leaving the session with no host directory\"),\n\t\t\t\t\t\t\tFlag.withDefault(false),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tserver: Flag.String(\"server\").pipe(\n\t\t\t\t\t\t\tFlag.withDescription(\"Base URL of the running codework server\"),\n\t\t\t\t\t\t\tFlag.withDefault(\"http://127.0.0.1:7433\"),\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t\texamples: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommand: \"codework session link ses_01h9\",\n\t\t\t\t\t\t\tdescription: \"Link it to the directory you are standing in\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommand: \"codework session link ses_01h9 ~/work/api\",\n\t\t\t\t\t\t\tdescription: \"Link it to a project elsewhere on this machine\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommand: \"codework session link ses_01h9 --unlink\",\n\t\t\t\t\t\t\tdescription: \"Clear the anchor\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t],\n\t\t}),\n\t\tSpec.make(\"plugin\", {\n\t\t\tdescription: \"Manage plugins\",\n\t\t\tcommands: [\n\t\t\t\tSpec.make(\"add\", {\n\t\t\t\t\tdescription: \"Install a plugin and add it to a settings file\",\n\t\t\t\t\tparams: {\n\t\t\t\t\t\tpackage: Argument.String(\"package\").pipe(\n\t\t\t\t\t\t\tArgument.withDescription(\"Package spec, path, or file: URL\"),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tglobal: Flag.Boolean(\"global\").pipe(\n\t\t\t\t\t\t\tFlag.withAlias(\"g\"),\n\t\t\t\t\t\t\tFlag.withDescription(\"Write the user-wide settings file instead of this project's\"),\n\t\t\t\t\t\t\tFlag.withDefault(false),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tsession: Flag.String(\"session\").pipe(\n\t\t\t\t\t\t\tFlag.withDescription(\"Write the project a session is linked to, rather than this directory's\"),\n\t\t\t\t\t\t\tFlag.optional,\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t\texamples: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommand: \"codework plugin add @acme/codework-tool-proc@1.2.0\",\n\t\t\t\t\t\t\tdescription: \"Install a published plugin into this project's settings\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommand: \"codework plugin add @acme/codework-tool-proc --session ses_01h9\",\n\t\t\t\t\t\t\tdescription: \"Write the project that session is linked to, from anywhere\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommand: \"codework plugin add @acme/codework-tool-proc -g\",\n\t\t\t\t\t\t\tdescription: \"Install it for every project this user runs\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t\tSpec.make(\"install\", {\n\t\t\t\t\tdescription: \"Install every plugin the settings files name\",\n\t\t\t\t\tparams: {},\n\t\t\t\t\texamples: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommand: \"codework plugin install\",\n\t\t\t\t\t\t\tdescription: \"Materialise what a fresh checkout already declares\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t\tSpec.make(\"list\", {\n\t\t\t\t\tdescription: \"List configured plugins and what each one resolved to\",\n\t\t\t\t\tparams: {\n\t\t\t\t\t\tverbose: Flag.Boolean(\"verbose\").pipe(\n\t\t\t\t\t\t\tFlag.withDescription(\"Show the message behind a failure, not only its reason\"),\n\t\t\t\t\t\t\tFlag.withDefault(false),\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t\texamples: [{ command: \"codework plugin list\", description: \"Show every configured plugin\" }],\n\t\t\t\t}),\n\t\t\t\tSpec.make(\"check\", {\n\t\t\t\t\tdescription: \"Report which configured plugins have a newer revision available\",\n\t\t\t\t\tparams: {\n\t\t\t\t\t\t// An answer is reused for an hour, which is right for a command someone may\n\t\t\t\t\t\t// run repeatedly and wrong for the minute after they pushed a plugin. Without\n\t\t\t\t\t\t// this there is no way to say \"ask again\", and `check` reports a moving branch\n\t\t\t\t\t\t// as current while `update` — which always refreshes — moves it.\n\t\t\t\t\t\trefresh: Flag.Boolean(\"refresh\").pipe(\n\t\t\t\t\t\t\tFlag.withDescription(\"Ask the remote again instead of reusing a recent answer\"),\n\t\t\t\t\t\t\tFlag.withDefault(false),\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t\texamples: [\n\t\t\t\t\t\t{ command: \"codework plugin check\", description: \"Ask the registry what has moved\" },\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommand: \"codework plugin check --refresh\",\n\t\t\t\t\t\t\tdescription: \"Ignore the cached answer, after pushing a plugin\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t\tSpec.make(\"update\", {\n\t\t\t\t\tdescription: \"Fetch a newer revision of every plugin that has one\",\n\t\t\t\t\tparams: {},\n\t\t\t\t\texamples: [{ command: \"codework plugin update\", description: \"Take whatever `check` found\" }],\n\t\t\t\t}),\n\t\t\t\tSpec.make(\"reload\", {\n\t\t\t\t\tdescription: \"Re-import configured plugins in a running server\",\n\t\t\t\t\tparams: {\n\t\t\t\t\t\tserver: Flag.String(\"server\").pipe(\n\t\t\t\t\t\t\tFlag.withDescription(\"Base URL of the running codework server\"),\n\t\t\t\t\t\t\tFlag.withDefault(\"http://127.0.0.1:7433\"),\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t\texamples: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommand: \"codework plugin reload\",\n\t\t\t\t\t\t\tdescription: \"Pick up edits to a local plugin without restarting\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t\tSpec.make(\"remove\", {\n\t\t\t\t\tdescription: \"Remove a plugin from a settings file\",\n\t\t\t\t\tparams: {\n\t\t\t\t\t\tpackage: Argument.String(\"package\").pipe(\n\t\t\t\t\t\t\tArgument.withDescription(\"Configured package spec, path, or plugin ID\"),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tglobal: Flag.Boolean(\"global\").pipe(\n\t\t\t\t\t\t\tFlag.withAlias(\"g\"),\n\t\t\t\t\t\t\tFlag.withDescription(\"Edit the user-wide settings file instead of this project's\"),\n\t\t\t\t\t\t\tFlag.withDefault(false),\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t\texamples: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommand: \"codework plugin remove @acme/codework-tool-proc\",\n\t\t\t\t\t\t\tdescription: \"Drop a plugin and any configuration written against it\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t],\n\t\t}),\n\t\tSpec.make(\"models\", {\n\t\t\tdescription: \"List or generate the model catalog\",\n\t\t\tparams: {\n\t\t\t\tprovider: Flag.String(\"provider\").pipe(Flag.withDescription(\"Model catalog provider ID\"), Flag.optional),\n\t\t\t},\n\t\t\texamples: [\n\t\t\t\t{ command: \"codework models\", description: \"List all models from all providers\" },\n\t\t\t\t{\n\t\t\t\t\tcommand: \"codework models --provider openai\",\n\t\t\t\t\tdescription: \"List models for a specific provider\",\n\t\t\t\t},\n\t\t\t\t{ command: \"codework models providers\", description: \"List all available provider IDs\" },\n\t\t\t],\n\t\t\tcommands: [\n\t\t\t\tSpec.make(\"providers\", {\n\t\t\t\t\tdescription: \"List available model providers in the catalog\",\n\t\t\t\t}),\n\t\t\t\tSpec.make(\"generate\", {\n\t\t\t\t\tdescription: \"Generate or update the model catalog\",\n\t\t\t\t\tparams: {\n\t\t\t\t\t\tpath: Argument.String(\"path\").pipe(\n\t\t\t\t\t\t\tArgument.withDescription(\n\t\t\t\t\t\t\t\t\"Output file or directory; defaults to CODEWORK_MODELS_FILE or ./models.gen.json\",\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tArgument.optional,\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t\texamples: [\n\t\t\t\t\t\t{ command: \"codework models generate\", description: \"Generate models.gen.json\" },\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommand: \"codework models generate .\",\n\t\t\t\t\t\t\tdescription: \"Generate models.gen.json in the current directory\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommand: \"codework models generate /path/to/models.gen.json\",\n\t\t\t\t\t\t\tdescription: \"Generate the catalog at an explicit path\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t],\n\t\t}),\n\t],\n});\n","import { Effect } from \"effect\";\nimport { Command } from \"effect/unstable/cli\";\nimport type { CommandError } from \"../cli/error.ts\";\nimport { Spec } from \"./spec.ts\";\n\n/**\n * Binds handlers to a {@link Spec} tree. Handlers are loaded through dynamic\n * imports so that starting the process -- or printing `--help` -- never pulls in\n * the harness, its sandbox drivers, or the model catalog.\n */\n\n/** Recovers the parsed input type of a spec node, for typing its handler. */\nexport type Input<Value> =\n\tValue extends Spec.Node<infer _Name, infer Spec, infer _Commands>\n\t\t? Input<Spec>\n\t\t: Value extends Command.Command<infer _Name, infer Parsed, infer _Context, infer _Error, infer _Requirements>\n\t\t\t? Parsed\n\t\t\t: never;\n\n/**\n * A handler may yield an ancestor's spec to read its shared flags, which shows up\n * as a `CommandContext` requirement; `withSubcommands` discharges it when the tree\n * is assembled.\n */\ntype Requirements = Command.Environment | Command.CommandContext<string>;\ntype Run<Parsed> = (input: Parsed) => Effect.Effect<void, CommandError, Requirements>;\ntype Loader<Node extends Spec.Any> = () => Promise<{ readonly default: Run<Input<Node>> }>;\n\n/**\n * The handler registry for a spec tree: a loader per leaf, mirroring the shape of\n * the tree. A parent with children takes its own handler under `$`.\n */\nexport type Handlers<Node extends Spec.Any> = keyof Node[\"commands\"] extends never\n\t? Loader<Node>\n\t: { readonly $?: Loader<Node> } & { readonly [Key in keyof Node[\"commands\"]]: Handlers<Node[\"commands\"][Key]> };\n\ninterface LazyHandler {\n\treadonly spec: Command.Command.Any;\n\t// biome-ignore lint/suspicious/noExplicitAny: input is recovered per-command by `Input`\n\treadonly load: () => Promise<{ readonly default: Run<any> }>;\n}\n\ntype LazyHandlers = LazyHandler[\"load\"] | { readonly [key: string]: LazyHandlers | undefined };\n\ntype ProvidedCommand = Command.Command<string, unknown, unknown, CommandError, Command.Environment>;\n\n/** Type-checks a handler against the spec node it implements. */\nexport function handler<const Node extends Spec.Any>(_node: Node, run: Run<Input<Node>>) {\n\treturn run;\n}\n\nexport function handlers<const Root extends Spec.Any>(\n\troot: Root,\n\thandlers: Handlers<Root>,\n): ReadonlyArray<LazyHandler> {\n\tconst result: LazyHandler[] = [];\n\n\tfunction add(node: Spec.Any, value: LazyHandlers) {\n\t\tif (typeof value === \"function\") {\n\t\t\tresult.push({ spec: node.spec, load: value });\n\t\t\treturn;\n\t\t}\n\t\tif (value.$ !== undefined) result.push({ spec: node.spec, load: value.$ as LazyHandler[\"load\"] });\n\t\tfor (const [name, child] of Object.entries(node.commands)) {\n\t\t\tconst nested = value[name];\n\t\t\tif (nested !== undefined) add(child, nested);\n\t\t}\n\t}\n\n\tadd(root, handlers as LazyHandlers);\n\treturn result;\n}\n\nexport function run(root: Spec.Any, handlers: ReadonlyArray<LazyHandler>, options: { readonly version: string }) {\n\treturn Command.run(provide(root, handlers), options);\n}\n\nfunction provide(node: Spec.Any, handlers: ReadonlyArray<LazyHandler>): ProvidedCommand {\n\tconst found = handlers.find((entry) => entry.spec === node.spec);\n\tconst spec = found\n\t\t? node.spec.pipe(\n\t\t\t\tCommand.withHandler((input) =>\n\t\t\t\t\tEffect.promise(found.load).pipe(Effect.flatMap((module) => module.default(input))),\n\t\t\t\t),\n\t\t\t)\n\t\t: node.spec;\n\tconst children = Object.values(node.commands);\n\tif (children.length === 0) return spec as ProvidedCommand;\n\treturn spec.pipe(Command.withSubcommands(children.map((child) => provide(child, handlers)))) as ProvidedCommand;\n}\n\nexport * as Runtime from \"./runtime.ts\";\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { t as harnessOptions } from "./harness-
|
|
1
|
+
import { A as layer$3, C as relink, D as register, E as list, O as resolve, S as link, T as drivers, X as Service$2, Z as Service$3, _ as Core, at as Service$1, b as create, dt as Prompt, g as Connected, h as Api, j as Service$5, k as stop, nt as SessionNotFoundError, ot as SubscriptionOverflowError, pt as schema_exports, r as renderError, tt as Service$4, u as writeError, v as encode, w as create$1, x as get, y as attach } from "./error-DTXBm1nO.mjs";
|
|
2
|
+
import { i as Cmd, t as handler } from "./runtime-CDSh4qOj.mjs";
|
|
3
|
+
import { t as harnessOptions } from "./harness-DQxbts5f.mjs";
|
|
4
4
|
import { Cause, Context, Effect, Layer, Option, Queue, Stream } from "effect";
|
|
5
5
|
import { NodeHttpServer } from "@effect/platform-node";
|
|
6
6
|
import { RpcSerialization, RpcServer } from "effect/unstable/rpc";
|
|
@@ -72,6 +72,7 @@ const toSessionInfo = (info) => ({
|
|
|
72
72
|
id: info.id,
|
|
73
73
|
title: info.title,
|
|
74
74
|
directory: info.directory,
|
|
75
|
+
...info.hostDir === void 0 ? {} : { hostDir: info.hostDir },
|
|
75
76
|
...info.sandbox === void 0 ? {} : { sandbox: toSandboxInfo(info.sandbox) }
|
|
76
77
|
});
|
|
77
78
|
const runtimeInput = (runtime) => ({
|
|
@@ -88,18 +89,23 @@ const layer$1 = Api.toLayer(Effect.gen(function* () {
|
|
|
88
89
|
const control = yield* Service$3;
|
|
89
90
|
const feed = yield* Service;
|
|
90
91
|
const sessions = yield* Service$4;
|
|
92
|
+
const state = yield* Service$5;
|
|
91
93
|
return Api.of({
|
|
92
|
-
"session.create": Effect.fnUntraced(function* ({ title, directory, sandbox, runtime }) {
|
|
94
|
+
"session.create": Effect.fnUntraced(function* ({ title, directory, hostDir, sandbox, runtime }) {
|
|
93
95
|
const selection = sandbox === void 0 ? void 0 : yield* resolve(sandbox);
|
|
94
96
|
const selected = selection?.info;
|
|
95
97
|
const handle = yield* create({
|
|
96
98
|
...title === void 0 ? {} : { title },
|
|
97
99
|
...directory === void 0 ? {} : { directory },
|
|
100
|
+
...hostDir === void 0 ? {} : { hostDir },
|
|
98
101
|
...selected === void 0 ? {} : { sandbox: selected },
|
|
99
102
|
...runtimeInput(runtime)
|
|
100
103
|
}).pipe(Effect.onError(() => releaseOnFailure(selection)));
|
|
101
104
|
return toSessionInfo(yield* handle.info);
|
|
102
105
|
}),
|
|
106
|
+
"plugin.reload": Effect.fnUntraced(function* () {
|
|
107
|
+
return yield* state.reload;
|
|
108
|
+
}),
|
|
103
109
|
"session.list": Effect.fnUntraced(function* () {
|
|
104
110
|
const rows = yield* sessions.list();
|
|
105
111
|
return yield* Effect.forEach(rows, (row) => sessionInfo(row.id).pipe(Effect.orDie));
|
|
@@ -114,6 +120,13 @@ const layer$1 = Api.toLayer(Effect.gen(function* () {
|
|
|
114
120
|
"session.info": Effect.fnUntraced(function* ({ sessionId }) {
|
|
115
121
|
return yield* sessionInfo(sessionId);
|
|
116
122
|
}),
|
|
123
|
+
"session.link": Effect.fnUntraced(function* ({ sessionId, hostDir }) {
|
|
124
|
+
const handle = yield* link({
|
|
125
|
+
sessionId,
|
|
126
|
+
hostDir: hostDir ?? null
|
|
127
|
+
});
|
|
128
|
+
return toSessionInfo(yield* handle.info);
|
|
129
|
+
}),
|
|
117
130
|
"session.relink": Effect.fnUntraced(function* ({ sessionId, sandbox, directory }) {
|
|
118
131
|
const selection = sandbox === void 0 ? void 0 : yield* resolve(sandbox);
|
|
119
132
|
const selected = selection?.info;
|
|
@@ -164,7 +177,7 @@ const layer$1 = Api.toLayer(Effect.gen(function* () {
|
|
|
164
177
|
yield* stop(sandboxId);
|
|
165
178
|
}),
|
|
166
179
|
"event.subscribe": () => Stream.unwrap(feed.subscribe.pipe(Effect.map((events) => Stream.make({
|
|
167
|
-
id: ID.create(),
|
|
180
|
+
id: schema_exports.EventSchema.ID.create(),
|
|
168
181
|
type: Connected.type,
|
|
169
182
|
data: {}
|
|
170
183
|
}).pipe(Stream.concat(events)))))
|
|
@@ -217,4 +230,4 @@ var serve_default = handler(Cmd.commands.serve, Effect.fn("CLI.serve")(function*
|
|
|
217
230
|
//#endregion
|
|
218
231
|
export { serve_default as default };
|
|
219
232
|
|
|
220
|
-
//# sourceMappingURL=serve-
|
|
233
|
+
//# sourceMappingURL=serve-DewC8gvV.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve-DewC8gvV.mjs","names":["Envelope.Core","Envelope.encode","Event.SubscriptionOverflowError","layer","Event.Service","EventRegistry.Service","Session.get","SessionStore.SessionNotFoundError","Sandbox.stop","layer","Control.Service","EventFeed.Service","SessionStore.Service","State.Service","Sandbox.resolve","Session.create","Session.attach","Session.link","Session.relink","Sandbox.drivers","Sandbox.list","Sandbox.create","Sandbox.register","EventSchema","Control.Service","Sandbox.list","Sandbox.stop","Contract.Api","EventFeed.layer","Harness.layer","Runtime.handler","Server.layer"],"sources":["../../src/server/feed.ts","../../src/server/handlers.ts","../../src/server/server.ts","../../src/cli/cmd/handlers/serve.ts"],"sourcesContent":["import { Event, EventRegistry, EventSchema } from \"@codeworksh/harness/effect\";\nimport { Cause, Context, Effect, Layer, Queue, Scope, Stream } from \"effect\";\nimport { EncodingError, type EventEnvelope, Envelope } from \"./envelope.ts\";\n\nexport const SubscriberCapacity = 4096;\n\nexport type Error = Event.SubscriptionOverflowError | EncodingError;\n\nexport interface Interface {\n\t/** A live envelope stream for one client; releasing the scope unregisters it. */\n\treadonly subscribe: Effect.Effect<Stream.Stream<EventEnvelope, Error>, never, Scope.Scope>;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@codeworksh/cli/server/feed/Service\") {}\n\n/**\n * Fan-out for `event.subscribe`. One listener on the event bus encodes each\n * event once and hands the same envelope to every client, rather than every\n * client re-encoding the same payload.\n *\n * Failure is per-cause, not per-feed: a client that cannot keep up fails alone\n * with its own overflow, while an event that cannot be encoded is a server bug\n * that disconnects everyone currently attached -- the feed stays usable for\n * whoever connects next. Types outside the server's registry are dropped\n * before they can consume a client's capacity.\n */\nexport const make = Effect.fn(\"EventFeed.make\")(function* (\n\tlisten: (subscriber: Event.Subscriber) => Effect.Effect<Event.Unsubscribe>,\n\toptions?: { readonly capacity?: number; readonly registry?: Envelope.Registry },\n) {\n\tconst capacity = options?.capacity ?? SubscriberCapacity;\n\t// Whatever this server knows how to describe, plugin registrations included.\n\tconst registry = options?.registry ?? Envelope.Core;\n\tconst subscribers = new Set<Queue.Queue<EventEnvelope, Error>>();\n\n\tconst failAll = (error: Error) =>\n\t\tEffect.sync(() => {\n\t\t\tconst current = Array.from(subscribers);\n\t\t\tsubscribers.clear();\n\t\t\tfor (const subscriber of current) Queue.failCauseUnsafe(subscriber, Cause.fail(error));\n\t\t});\n\n\tconst deliver = Effect.fnUntraced(function* (event: EventSchema.Payload) {\n\t\tif (subscribers.size === 0) return;\n\t\t// Dropped here rather than after the queue: a burst of events this server\n\t\t// cannot describe must not spend a client's capacity on its way to nowhere.\n\t\tif (registry(event.type) === undefined) return;\n\t\tconst envelope = yield* Envelope.encode(event, registry);\n\t\tfor (const subscriber of subscribers) {\n\t\t\tif (Queue.offerUnsafe(subscriber, envelope)) continue;\n\t\t\tsubscribers.delete(subscriber);\n\t\t\tQueue.failCauseUnsafe(subscriber, Cause.fail(new Event.SubscriptionOverflowError({ capacity })));\n\t\t}\n\t});\n\n\tconst publish = (event: EventSchema.Payload) =>\n\t\tdeliver(event).pipe(\n\t\t\tEffect.catchTags({\n\t\t\t\tEventEncodingError: (error) =>\n\t\t\t\t\tEffect.logError(\"Failed to encode public event\", { type: error.type, cause: error.cause }).pipe(\n\t\t\t\t\t\tEffect.andThen(failAll(error)),\n\t\t\t\t\t),\n\t\t\t\t// The registry guard in `deliver` already returned for unknown types.\n\t\t\t\tUnknownEventTypeError: Effect.die,\n\t\t\t}),\n\t\t);\n\n\tconst unsubscribe = yield* listen(publish);\n\tyield* Effect.addFinalizer(() => unsubscribe);\n\n\treturn Service.of({\n\t\tsubscribe: Effect.acquireRelease(\n\t\t\tQueue.dropping<EventEnvelope, Error>(capacity).pipe(\n\t\t\t\tEffect.tap((queue) => Effect.sync(() => subscribers.add(queue))),\n\t\t\t),\n\t\t\t(queue) => Effect.sync(() => subscribers.delete(queue)).pipe(Effect.andThen(Queue.shutdown(queue))),\n\t\t).pipe(Effect.map(Stream.fromQueue)),\n\t});\n});\n\nexport const layer = Layer.effect(\n\tService,\n\tEffect.gen(function* () {\n\t\tconst events = yield* Event.Service;\n\t\tconst registered = yield* EventRegistry.Service;\n\t\treturn yield* make(events.listen, { registry: registered.get });\n\t}),\n);\n\nexport * as EventFeed from \"./feed.ts\";\n","import { Control, EventSchema, PromptSchema, Sandbox, Session, SessionStore, State } from \"@codeworksh/harness/effect\";\nimport { Effect, Option, Stream } from \"effect\";\nimport { Contract, type RuntimeConfig, type SandboxInfo, type SessionInfo } from \"./contract.ts\";\nimport { Envelope } from \"./envelope.ts\";\nimport { EventFeed } from \"./feed.ts\";\n\nconst toSandboxInfo = (info: Sandbox.Info): SandboxInfo => ({\n\tid: info.id,\n\tdriver: info.driver,\n\tkind: info.kind,\n\tproviderResourceId: info.providerResourceId,\n\townership: info.ownership,\n\tstatus: info.status,\n\tusage: info.usage,\n\trefCount: info.refCount,\n\tcreatedAt: info.createdAt,\n\tupdatedAt: info.updatedAt,\n});\n\nconst toSessionInfo = (info: Session.Info): SessionInfo => ({\n\tid: info.id,\n\ttitle: info.title,\n\tdirectory: info.directory,\n\t...(info.hostDir === undefined ? {} : { hostDir: info.hostDir }),\n\t...(info.sandbox === undefined ? {} : { sandbox: toSandboxInfo(info.sandbox) }),\n});\n\n// Wire configs name only the keys they set, so `undefined` never reaches the\n// harness inputs under exactOptionalPropertyTypes.\nconst runtimeInput = (runtime: RuntimeConfig | undefined): Session.RuntimeInput => ({\n\t...(runtime?.model === undefined ? {} : { model: runtime.model }),\n\t...(runtime?.thinkingLevel === undefined ? {} : { thinkingLevel: runtime.thinkingLevel }),\n});\n\nconst sessionInfo = Effect.fn(\"Server.sessionInfo\")(function* (sessionId: Session.SessionSchema.ID) {\n\tconst found = yield* Session.get(sessionId);\n\tif (Option.isNone(found)) return yield* new SessionStore.SessionNotFoundError({ sessionId });\n\treturn toSessionInfo(yield* found.value.info);\n});\n\nconst releaseOnFailure = (selection: Effect.Success<ReturnType<typeof Sandbox.resolve>> | undefined) =>\n\tselection?.created && selection.info !== undefined\n\t\t? Sandbox.stop(selection.info.id).pipe(Effect.catchCause(Effect.logError))\n\t\t: Effect.void;\n\nexport const layer = Contract.Api.toLayer(\n\tEffect.gen(function* () {\n\t\tconst control = yield* Control.Service;\n\t\tconst feed = yield* EventFeed.Service;\n\t\tconst sessions = yield* SessionStore.Service;\n\t\tconst state = yield* State.Service;\n\n\t\treturn Contract.Api.of({\n\t\t\t\"session.create\": Effect.fnUntraced(function* ({ title, directory, hostDir, sandbox, runtime }) {\n\t\t\t\tconst selection = sandbox === undefined ? undefined : yield* Sandbox.resolve(sandbox);\n\t\t\t\tconst selected = selection?.info;\n\t\t\t\tconst handle = yield* Session.create({\n\t\t\t\t\t...(title === undefined ? {} : { title }),\n\t\t\t\t\t...(directory === undefined ? {} : { directory }),\n\t\t\t\t\t// Passed through as given, never defaulted to the server's own directory: a\n\t\t\t\t\t// session the client did not place has no project, which is a normal state.\n\t\t\t\t\t...(hostDir === undefined ? {} : { hostDir }),\n\t\t\t\t\t...(selected === undefined ? {} : { sandbox: selected }),\n\t\t\t\t\t...runtimeInput(runtime),\n\t\t\t\t}).pipe(Effect.onError(() => releaseOnFailure(selection)));\n\t\t\t\treturn toSessionInfo(yield* handle.info);\n\t\t\t}),\n\t\t\t\"plugin.reload\": Effect.fnUntraced(function* () {\n\t\t\t\treturn yield* state.reload;\n\t\t\t}),\n\t\t\t\"session.list\": Effect.fnUntraced(function* () {\n\t\t\t\tconst rows = yield* sessions.list();\n\t\t\t\treturn yield* Effect.forEach(rows, (row) => sessionInfo(row.id).pipe(Effect.orDie));\n\t\t\t}),\n\t\t\t\"session.configure\": Effect.fnUntraced(function* ({ sessionId, runtime }) {\n\t\t\t\tconst handle = yield* Session.attach({ sessionId, ...runtimeInput(runtime) });\n\t\t\t\treturn toSessionInfo(yield* handle.info);\n\t\t\t}),\n\t\t\t\"session.info\": Effect.fnUntraced(function* ({ sessionId }) {\n\t\t\t\treturn yield* sessionInfo(sessionId);\n\t\t\t}),\n\t\t\t\"session.link\": Effect.fnUntraced(function* ({ sessionId, hostDir }) {\n\t\t\t\tconst handle = yield* Session.link({ sessionId, hostDir: hostDir ?? null });\n\t\t\t\treturn toSessionInfo(yield* handle.info);\n\t\t\t}),\n\t\t\t\"session.relink\": Effect.fnUntraced(function* ({ sessionId, sandbox, directory }) {\n\t\t\t\tconst selection = sandbox === undefined ? undefined : yield* Sandbox.resolve(sandbox);\n\t\t\t\tconst selected = selection?.info;\n\t\t\t\tconst handle = yield* Session.relink({\n\t\t\t\t\tsessionId,\n\t\t\t\t\t...(selected === undefined ? {} : { sandbox: selected }),\n\t\t\t\t\t...(directory === undefined ? {} : { directory }),\n\t\t\t\t}).pipe(Effect.onError(() => releaseOnFailure(selection)));\n\t\t\t\treturn toSessionInfo(yield* handle.info);\n\t\t\t}),\n\t\t\t\"session.prompt\": Effect.fnUntraced(function* ({ sessionId, text, delivery, id }) {\n\t\t\t\tyield* control.prompt({\n\t\t\t\t\tsessionId,\n\t\t\t\t\tprompt: PromptSchema.Prompt.make({ text }),\n\t\t\t\t\t...(delivery === undefined ? {} : { delivery }),\n\t\t\t\t\t...(id === undefined ? {} : { id }),\n\t\t\t\t});\n\t\t\t}),\n\t\t\t\"session.interrupt\": Effect.fnUntraced(function* ({ sessionId }) {\n\t\t\t\t// Ack only: the wire answers \"was there work to stop\", and the caller\n\t\t\t\t// uses `session.wait` when it needs cleanup to have finished.\n\t\t\t\treturn { interrupted: yield* control.interrupt(sessionId, { reason: \"user\" }) };\n\t\t\t}),\n\t\t\t\"session.message\": Effect.fnUntraced(function* ({ sessionId, messageId }) {\n\t\t\t\tconst found = yield* sessions.entry(messageId);\n\t\t\t\t// Scoped to the session the caller named: an entry id is global, and a\n\t\t\t\t// client should not learn about conversations it did not ask for.\n\t\t\t\treturn { found: Option.isSome(found) && found.value.entry.sessionId === sessionId };\n\t\t\t}),\n\t\t\t\"session.wait\": Effect.fnUntraced(function* ({ sessionId }) {\n\t\t\t\tyield* control.wait(sessionId);\n\t\t\t}),\n\t\t\t\"sandbox.drivers\": Effect.fnUntraced(function* () {\n\t\t\t\tconst drivers = yield* Sandbox.drivers();\n\t\t\t\treturn drivers.map(({ name, kind }) => ({ name, kind }));\n\t\t\t}),\n\t\t\t\"sandbox.list\": Effect.fnUntraced(function* () {\n\t\t\t\treturn (yield* Sandbox.list()).map(toSandboxInfo);\n\t\t\t}),\n\t\t\t\"sandbox.create\": Effect.fnUntraced(function* ({ driver }) {\n\t\t\t\treturn toSandboxInfo(yield* Sandbox.create({ driver }));\n\t\t\t}),\n\t\t\t\"sandbox.register\": Effect.fnUntraced(function* ({ driver, providerResourceId }) {\n\t\t\t\treturn toSandboxInfo(yield* Sandbox.register({ driver, providerResourceId }));\n\t\t\t}),\n\t\t\t\"sandbox.stop\": Effect.fnUntraced(function* ({ sandboxId }) {\n\t\t\t\tyield* Sandbox.stop(sandboxId);\n\t\t\t}),\n\t\t\t// Acquiring the feed subscription is the readiness barrier: `server.connected`\n\t\t\t// is only emitted once this client is registered, so a prompt admitted after\n\t\t\t// it cannot land in the gap.\n\t\t\t\"event.subscribe\": () =>\n\t\t\t\tStream.unwrap(\n\t\t\t\t\tfeed.subscribe.pipe(\n\t\t\t\t\t\tEffect.map((events) =>\n\t\t\t\t\t\t\tStream.make({ id: EventSchema.ID.create(), type: Envelope.Connected.type, data: {} }).pipe(\n\t\t\t\t\t\t\t\tStream.concat(events),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t});\n\t}),\n);\n\nexport * as Handlers from \"./handlers.ts\";\n","import { Control, Harness, Sandbox } from \"@codeworksh/harness/effect\";\nimport { NodeHttpServer } from \"@effect/platform-node\";\nimport { Effect, Layer } from \"effect\";\nimport { HttpRouter, HttpServer } from \"effect/unstable/http\";\nimport * as NetAddress from \"effect/unstable/net/NetAddress\";\nimport { RpcSerialization, RpcServer } from \"effect/unstable/rpc\";\n/* @effect-diagnostics nodeBuiltinImport:off -- the RPC listener needs a raw node server. */\nimport { createServer } from \"node:http\";\nimport { Contract } from \"./contract.ts\";\nimport { EventFeed } from \"./feed.ts\";\nimport { Handlers } from \"./handlers.ts\";\n\nconst WsProtocol = RpcServer.layerProtocolWebsocket({ path: \"/rpc\" }).pipe(Layer.provide(HttpRouter.layer));\n\n// Managed instances outlive any single RPC; the serve scope is what stops them.\nconst managedShutdown = Layer.unwrap(\n\tEffect.gen(function* () {\n\t\tconst control = yield* Control.Service;\n\t\tyield* Effect.addFinalizer(() =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\t// Release drain leases before stopping the server's managed sandboxes.\n\t\t\t\t// Both phases are best effort and bounded so a provider cannot prevent\n\t\t\t\t// process shutdown; an interrupted stop is persisted as faulted and can\n\t\t\t\t// recover the next time that sandbox is mounted.\n\t\t\t\tyield* Effect.forEach(\n\t\t\t\t\tyield* control.active,\n\t\t\t\t\t(id) =>\n\t\t\t\t\t\tcontrol\n\t\t\t\t\t\t\t.interrupt(id, { reason: \"shutdown\", awaitSettlement: true })\n\t\t\t\t\t\t\t.pipe(Effect.timeout(\"10 seconds\"), Effect.ignore),\n\t\t\t\t\t{ concurrency: \"unbounded\", discard: true },\n\t\t\t\t);\n\t\t\t\tconst infos = yield* Sandbox.list();\n\t\t\t\tyield* Effect.forEach(\n\t\t\t\t\tinfos.filter((info) => info.ownership === \"managed\"),\n\t\t\t\t\t(info) =>\n\t\t\t\t\t\tSandbox.stop(info.id).pipe(\n\t\t\t\t\t\t\tEffect.timeout(\"10 seconds\"),\n\t\t\t\t\t\t\tEffect.catchCause((cause) =>\n\t\t\t\t\t\t\t\tEffect.logError(\"Failed to stop managed sandbox during shutdown\", cause).pipe(\n\t\t\t\t\t\t\t\t\tEffect.annotateLogs({ sandboxInstanceId: info.id }),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t{ concurrency: \"unbounded\", discard: true },\n\t\t\t\t);\n\t\t\t}),\n\t\t);\n\t\treturn Layer.empty;\n\t}),\n);\n\nconst listenLog = Layer.unwrap(\n\tEffect.gen(function* () {\n\t\tconst server = yield* HttpServer.HttpServer;\n\t\tyield* Effect.log(`codework serve listening on ${NetAddress.formatUrlUnsafe(server.address, \"ws\")}/rpc`);\n\t\treturn Layer.empty;\n\t}),\n);\n\nexport const layer = (options: { host: string; port: number; harness: Harness.Options }) => {\n\tconst rpc = RpcServer.layer(Contract.Api).pipe(Layer.provide(Handlers.layer.pipe(Layer.provide(EventFeed.layer))));\n\tconst app = rpc.pipe(\n\t\tLayer.provideMerge(WsProtocol),\n\t\tLayer.provide(HttpRouter.serve(WsProtocol, { disableListenLog: true })),\n\t\tLayer.provideMerge(managedShutdown),\n\t);\n\treturn app.pipe(\n\t\tLayer.provideMerge(listenLog),\n\t\tLayer.provideMerge(NodeHttpServer.layer(() => createServer(), { host: options.host, port: options.port })),\n\t\tLayer.provide(RpcSerialization.layerNdjson),\n\t\tLayer.provide(Harness.layer(options.harness)),\n\t);\n};\n\nexport * as Server from \"./server.ts\";\n","import { Effect, Layer } from \"effect\";\nimport { Runtime } from \"../../../framework/runtime.ts\";\nimport { Server } from \"../../../server/server.ts\";\nimport { renderError } from \"../../error.ts\";\nimport { harnessOptions } from \"../../harness.ts\";\nimport { writeError } from \"../../output.ts\";\nimport { Cmd } from \"../cmd.ts\";\n\nexport default Runtime.handler(\n\tCmd.commands.serve,\n\tEffect.fn(\"CLI.serve\")(function* ({ host, port }) {\n\t\tconst shared = yield* Cmd.spec;\n\t\tyield* Layer.launch(Server.layer({ host, port, harness: harnessOptions(shared) })).pipe(\n\t\t\tEffect.catch((error) =>\n\t\t\t\twriteError(renderError(error)).pipe(\n\t\t\t\t\tEffect.andThen(\n\t\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\t\tprocess.exitCode = 1;\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t}),\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { B as required, I as canonical, N as paths, P as projectRoot, R as inspect, U as lock, W as parse$1, gt as resolve, t as InvalidInputError } from "./error-DTXBm1nO.mjs";
|
|
2
|
+
import { Effect, FileSystem, Option, Path } from "effect";
|
|
3
|
+
import { applyEdits, modify, parse } from "jsonc-parser";
|
|
4
|
+
//#region src/cli/cmd/handlers/plugin/settings.ts
|
|
5
|
+
/** The module an entry names: a bare loader string, or the `package` of a config object. */
|
|
6
|
+
const moduleOf = (entry) => {
|
|
7
|
+
if (typeof entry === "string") return entry;
|
|
8
|
+
if (typeof entry !== "object" || entry === null) return void 0;
|
|
9
|
+
const { package: located } = entry;
|
|
10
|
+
return typeof located === "string" ? located : void 0;
|
|
11
|
+
};
|
|
12
|
+
/** The plugin ID an entry configures, if it names one. */
|
|
13
|
+
const idOf = (entry) => {
|
|
14
|
+
if (typeof entry !== "object" || entry === null) return void 0;
|
|
15
|
+
const { plugin } = entry;
|
|
16
|
+
return typeof plugin === "string" ? plugin : void 0;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Every spelling a reference answers to, without touching the filesystem.
|
|
20
|
+
*
|
|
21
|
+
* `canonical` drops a version suffix and resolves a relative path against the file that declared
|
|
22
|
+
* it, so `@acme/x@1.2.0` and `@acme/x` are one name. The literal string is kept alongside it
|
|
23
|
+
* because a plugin ID is not a module reference and survives `canonical` unchanged.
|
|
24
|
+
*/
|
|
25
|
+
const aliases = (reference, directory) => canonical(reference, directory).pipe(Effect.map((canonical) => [reference, canonical]), Effect.orElseSucceed(() => [reference]));
|
|
26
|
+
/** Every spelling `reference` answers to, computed once for a whole command. */
|
|
27
|
+
const spellings = (reference, directory) => Effect.map(aliases(reference, directory), (found) => new Set(found));
|
|
28
|
+
/**
|
|
29
|
+
* What each configured entry answers to.
|
|
30
|
+
*
|
|
31
|
+
* Comparing the string as written is not enough. `plugin add @acme/x@1.2.0` records a version the
|
|
32
|
+
* user will not repeat when removing it, and one plugin can be named three ways: by package, by
|
|
33
|
+
* path, or by the ID it declares -- a `{ "plugin": "<id>" }` configuration object sits next to the
|
|
34
|
+
* module entry that loaded it, and removing the plugin has to take both.
|
|
35
|
+
*
|
|
36
|
+
* The declared ID is only knowable by importing the module, so it is read from what is already
|
|
37
|
+
* installed: `resolveCached` reads a published package out of the harness cache and never runs an
|
|
38
|
+
* installer, so editing a settings file cannot fetch anything. An entry that does not resolve --
|
|
39
|
+
* uninstalled, moved, or broken -- keeps the names it was written with rather than failing the
|
|
40
|
+
* command, because an unusable plugin is exactly the one a user is trying to remove.
|
|
41
|
+
*/
|
|
42
|
+
const identify = Effect.fn("CLI.plugin.identify")(function* (plugins, file, cache) {
|
|
43
|
+
const directory = (yield* Path.Path).dirname(file);
|
|
44
|
+
const entries = [];
|
|
45
|
+
for (const value of plugins) {
|
|
46
|
+
const answers = /* @__PURE__ */ new Set();
|
|
47
|
+
const id = idOf(value);
|
|
48
|
+
if (id !== void 0) answers.add(id);
|
|
49
|
+
const module = moduleOf(value);
|
|
50
|
+
if (module !== void 0) {
|
|
51
|
+
for (const alias of yield* aliases(module, directory)) answers.add(alias);
|
|
52
|
+
const declared = yield* inspect(module, {
|
|
53
|
+
cache,
|
|
54
|
+
hostDir: directory,
|
|
55
|
+
file,
|
|
56
|
+
install: required
|
|
57
|
+
}).pipe(Effect.option);
|
|
58
|
+
if (Option.isSome(declared)) {
|
|
59
|
+
answers.add(declared.value.id);
|
|
60
|
+
if (declared.value.name !== void 0) answers.add(declared.value.name);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
entries.push({
|
|
64
|
+
value,
|
|
65
|
+
answers,
|
|
66
|
+
loads: typeof value === "string"
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return entries;
|
|
70
|
+
});
|
|
71
|
+
/**
|
|
72
|
+
* What a local reference should be *written* as, which is rarely what was typed.
|
|
73
|
+
*
|
|
74
|
+
* A relative path on the command line is relative to `cwd`; in a settings file it is relative to
|
|
75
|
+
* that file's own directory. Those differ the moment the command runs anywhere but the directory
|
|
76
|
+
* holding the file, and writing the string verbatim produces a silently wrong entry:
|
|
77
|
+
*
|
|
78
|
+
* ```
|
|
79
|
+
* $ cd ~/workspace/app/packages/fubar
|
|
80
|
+
* $ codework plugin add ./plugins/x.ts # means packages/fubar/plugins/x.ts
|
|
81
|
+
* → the project is ~/workspace/app, so the entry lands in app/.codework/settings.jsonc
|
|
82
|
+
* → written verbatim, the harness loads app/.codework/plugins/x.ts ← a different file
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* With both ends inside one project the entry is written relative to the file, which is portable
|
|
86
|
+
* to any checkout of that repository and therefore worth committing. Otherwise -- a `-g` write
|
|
87
|
+
* into `<home>`, a `--user-config-dir` elsewhere, or a target outside the project -- relative
|
|
88
|
+
* would be a lie, and an absolute path means the same thing read from anywhere.
|
|
89
|
+
*
|
|
90
|
+
* A package spec is returned untouched: it names no location.
|
|
91
|
+
*/
|
|
92
|
+
const written = Effect.fn("CLI.plugin.written")(function* (reference, input) {
|
|
93
|
+
const nodePath = yield* Path.Path;
|
|
94
|
+
const target = yield* parse$1(reference, input.cwd).pipe(Effect.option);
|
|
95
|
+
if (Option.isNone(target) || target.value.kind !== "local") return reference;
|
|
96
|
+
const absolute = target.value.path;
|
|
97
|
+
const inside = (root, path) => path === root || path.startsWith(`${root}${nodePath.sep}`);
|
|
98
|
+
if (input.root !== void 0 && inside(input.root, input.file) && inside(input.root, absolute)) {
|
|
99
|
+
const relative = nodePath.relative(nodePath.dirname(input.file), absolute);
|
|
100
|
+
return relative.startsWith("..") ? relative : `./${relative}`;
|
|
101
|
+
}
|
|
102
|
+
return absolute;
|
|
103
|
+
});
|
|
104
|
+
/** `reference` selects `entry` when any spelling of one is a spelling of the other. */
|
|
105
|
+
const matches = (entry, spelled) => [...spelled].some((alias) => entry.answers.has(alias));
|
|
106
|
+
/** Every entry connected to a reference through a package spelling or declared plugin ID. */
|
|
107
|
+
const matching = (entries, spelled) => {
|
|
108
|
+
const answers = new Set(spelled);
|
|
109
|
+
const found = /* @__PURE__ */ new Set();
|
|
110
|
+
for (;;) {
|
|
111
|
+
const next = entries.filter((entry) => !found.has(entry) && [...entry.answers].some((answer) => answers.has(answer)));
|
|
112
|
+
if (next.length === 0) return found;
|
|
113
|
+
for (const entry of next) {
|
|
114
|
+
found.add(entry);
|
|
115
|
+
for (const answer of entry.answers) answers.add(answer);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const readPlugins = Effect.fn("CLI.plugin.readPlugins")(function* (path) {
|
|
120
|
+
const fs = yield* FileSystem.FileSystem;
|
|
121
|
+
const source = (yield* fs.exists(path)) ? yield* fs.readFileString(path) : "{}";
|
|
122
|
+
const errors = [];
|
|
123
|
+
const document = parse(source, errors, { allowTrailingComma: true });
|
|
124
|
+
if (errors.length > 0 || typeof document !== "object" || document === null || Array.isArray(document)) return yield* new InvalidInputError({ message: `${path} is not a valid settings file` });
|
|
125
|
+
const plugins = document.plugins;
|
|
126
|
+
if (plugins !== void 0 && !Array.isArray(plugins)) return yield* new InvalidInputError({ message: `${path}: "plugins" must be an array` });
|
|
127
|
+
return {
|
|
128
|
+
source,
|
|
129
|
+
plugins: plugins ?? []
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
const writePlugins = Effect.fn("CLI.plugin.writePlugins")(function* (path, source, plugins) {
|
|
133
|
+
const fs = yield* FileSystem.FileSystem;
|
|
134
|
+
const nodePath = yield* Path.Path;
|
|
135
|
+
const edited = applyEdits(source, modify(source, ["plugins"], plugins, { formattingOptions: {
|
|
136
|
+
tabSize: 2,
|
|
137
|
+
insertSpaces: false
|
|
138
|
+
} }));
|
|
139
|
+
const directory = nodePath.dirname(path);
|
|
140
|
+
yield* fs.makeDirectory(directory, { recursive: true });
|
|
141
|
+
const mode = (yield* fs.exists(path)) ? (yield* fs.stat(path)).mode & 511 : 384;
|
|
142
|
+
const temporary = yield* fs.makeTempFile({
|
|
143
|
+
directory,
|
|
144
|
+
prefix: `.${nodePath.basename(path)}.`,
|
|
145
|
+
suffix: ".tmp"
|
|
146
|
+
});
|
|
147
|
+
yield* fs.writeFileString(temporary, edited.endsWith("\n") ? edited : `${edited}\n`).pipe(Effect.andThen(fs.chmod(temporary, mode)), Effect.andThen(fs.rename(temporary, path)), Effect.ensuring(fs.remove(temporary, { force: true }).pipe(Effect.ignore)));
|
|
148
|
+
});
|
|
149
|
+
/**
|
|
150
|
+
* Serialize one settings read-modify-rename across CLI processes.
|
|
151
|
+
*
|
|
152
|
+
* The install or inspection that precedes an edit stays outside this scope; only the local edit is
|
|
153
|
+
* locked, so unrelated downloads do not wait behind one another.
|
|
154
|
+
*/
|
|
155
|
+
const withPluginsLock = (path, edit) => Effect.gen(function* () {
|
|
156
|
+
const fs = yield* FileSystem.FileSystem;
|
|
157
|
+
const nodePath = yield* Path.Path;
|
|
158
|
+
yield* fs.makeDirectory(nodePath.dirname(path), { recursive: true });
|
|
159
|
+
yield* lock(`${path}.lock`, () => new InvalidInputError({ message: `timed out waiting to edit ${path}` }));
|
|
160
|
+
return yield* edit;
|
|
161
|
+
}).pipe(Effect.scoped);
|
|
162
|
+
/**
|
|
163
|
+
* Which file the edit lands in.
|
|
164
|
+
*
|
|
165
|
+
* A plugin belongs to a project by default — it is part of how that repository is worked on, so
|
|
166
|
+
* the entry belongs in a file the repository can commit. `--global` writes the user-wide file
|
|
167
|
+
* instead, the way `npm -g` installs outside a package. An explicit `--user-config-dir` outranks
|
|
168
|
+
* both, since naming a settings directory is already a decision about where settings live.
|
|
169
|
+
*
|
|
170
|
+
* The project is found the way the harness finds it: the nearest ancestor holding a `.codework`
|
|
171
|
+
* directory, so a command run from `packages/app` edits the repository's own file rather than
|
|
172
|
+
* creating a second one beside it. When no ancestor has one, the project begins here -- the
|
|
173
|
+
* directory is created and the caller is told, because that decision changes where every future
|
|
174
|
+
* plugin entry lands, and a mistyped `cd` is the way it goes wrong.
|
|
175
|
+
*
|
|
176
|
+
* There is nothing to warn about between layers: plugin entries from every layer accumulate, so a
|
|
177
|
+
* user-wide entry still applies inside a project that declares its own.
|
|
178
|
+
*/
|
|
179
|
+
const resolveTarget = Effect.fn("CLI.plugin.resolveTarget")(function* (shared, global, from, create = true) {
|
|
180
|
+
const fs = yield* FileSystem.FileSystem;
|
|
181
|
+
const nodePath = yield* Path.Path;
|
|
182
|
+
const cwd = from ?? nodePath.resolve(".");
|
|
183
|
+
const home = yield* resolve(Option.isNone(shared.home) ? {} : { home: shared.home.value });
|
|
184
|
+
const root = global ? void 0 : yield* projectRoot(cwd, home.home);
|
|
185
|
+
const groups = paths({
|
|
186
|
+
home: home.home,
|
|
187
|
+
...root === void 0 ? {} : { root },
|
|
188
|
+
from: cwd,
|
|
189
|
+
...Option.isNone(shared.userConfigDir) ? {} : { custom: shared.userConfigDir.value }
|
|
190
|
+
});
|
|
191
|
+
const found = groups[Option.isSome(shared.userConfigDir) ? groups.length - 1 : global ? 0 : 1] ?? [];
|
|
192
|
+
if (found.length > 0) {
|
|
193
|
+
const existing = yield* Effect.findFirst(found, (candidate) => fs.exists(candidate));
|
|
194
|
+
return {
|
|
195
|
+
path: Option.getOrElse(existing, () => found[0]),
|
|
196
|
+
...root === void 0 ? {} : { root }
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const marker = nodePath.join(cwd, ".codework");
|
|
200
|
+
if (!create) return {
|
|
201
|
+
path: nodePath.join(marker, "settings.jsonc"),
|
|
202
|
+
created: marker,
|
|
203
|
+
root: cwd
|
|
204
|
+
};
|
|
205
|
+
yield* fs.makeDirectory(marker, { recursive: true });
|
|
206
|
+
return {
|
|
207
|
+
path: nodePath.join(marker, "settings.jsonc"),
|
|
208
|
+
created: marker,
|
|
209
|
+
root: cwd
|
|
210
|
+
};
|
|
211
|
+
});
|
|
212
|
+
//#endregion
|
|
213
|
+
export { resolveTarget as a, writePlugins as c, readPlugins as i, written as l, matches as n, spellings as o, matching as r, withPluginsLock as s, identify as t };
|
|
214
|
+
|
|
215
|
+
//# sourceMappingURL=settings-Bn2pmQXX.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-Bn2pmQXX.mjs","names":["Plugin.canonical","Plugin.inspect","Plugin.resolveCached","Plugin.parse","parseJsonc","Plugin.settingsLock","Global.resolve","Settings.projectRoot","Settings.paths"],"sources":["../../src/cli/cmd/handlers/plugin/settings.ts"],"sourcesContent":["import { Global, Plugin, Settings } from \"@codeworksh/harness/effect\";\nimport { Effect, FileSystem, Option, Path } from \"effect\";\nimport { applyEdits, modify, parse as parseJsonc, type ParseError } from \"jsonc-parser\";\nimport { InvalidInputError } from \"../../../error.ts\";\n\n/**\n * Reading and rewriting the `plugins` array of one settings file.\n *\n * Edits go through `jsonc-parser`, so a hand-written file keeps its comments, key order and\n * formatting: a plugin added by the CLI must not reformat a file its owner maintains. The write\n * is a temp file renamed over the original, so an interrupted run leaves the previous settings\n * intact rather than a half-written document.\n */\n\nexport interface Shared {\n\treadonly userConfigDir: Option.Option<string>;\n\treadonly home: Option.Option<string>;\n}\n\nexport interface Target {\n\treadonly path: string;\n\t/** Set when this command had to create the project directory, so the caller can say so. */\n\treadonly created?: string;\n\t/** The project this file belongs to, which a local entry is written relative to. */\n\treadonly root?: string;\n}\n\n/** The module an entry names: a bare loader string, or the `package` of a config object. */\nconst moduleOf = (entry: unknown): string | undefined => {\n\tif (typeof entry === \"string\") return entry;\n\tif (typeof entry !== \"object\" || entry === null) return undefined;\n\tconst { package: located } = entry as { package?: unknown };\n\treturn typeof located === \"string\" ? located : undefined;\n};\n\n/** The plugin ID an entry configures, if it names one. */\nconst idOf = (entry: unknown): string | undefined => {\n\tif (typeof entry !== \"object\" || entry === null) return undefined;\n\tconst { plugin } = entry as { plugin?: unknown };\n\treturn typeof plugin === \"string\" ? plugin : undefined;\n};\n\n/**\n * Every spelling a reference answers to, without touching the filesystem.\n *\n * `canonical` drops a version suffix and resolves a relative path against the file that declared\n * it, so `@acme/x@1.2.0` and `@acme/x` are one name. The literal string is kept alongside it\n * because a plugin ID is not a module reference and survives `canonical` unchanged.\n */\nconst aliases = (reference: string, directory: string): Effect.Effect<ReadonlyArray<string>> =>\n\tPlugin.canonical(reference, directory).pipe(\n\t\tEffect.map((canonical) => [reference, canonical] as ReadonlyArray<string>),\n\t\t// A reference that does not parse still answers to itself: a plugin ID is not a module\n\t\t// reference, and it has to keep matching an entry that names it.\n\t\tEffect.orElseSucceed(() => [reference] as ReadonlyArray<string>),\n\t);\n\n/** Every spelling `reference` answers to, computed once for a whole command. */\nexport const spellings = (reference: string, directory: string): Effect.Effect<ReadonlySet<string>> =>\n\tEffect.map(aliases(reference, directory), (found) => new Set(found));\n\nexport interface Entry {\n\treadonly value: unknown;\n\treadonly answers: ReadonlySet<string>;\n\treadonly loads: boolean;\n}\n\n/**\n * What each configured entry answers to.\n *\n * Comparing the string as written is not enough. `plugin add @acme/x@1.2.0` records a version the\n * user will not repeat when removing it, and one plugin can be named three ways: by package, by\n * path, or by the ID it declares -- a `{ \"plugin\": \"<id>\" }` configuration object sits next to the\n * module entry that loaded it, and removing the plugin has to take both.\n *\n * The declared ID is only knowable by importing the module, so it is read from what is already\n * installed: `resolveCached` reads a published package out of the harness cache and never runs an\n * installer, so editing a settings file cannot fetch anything. An entry that does not resolve --\n * uninstalled, moved, or broken -- keeps the names it was written with rather than failing the\n * command, because an unusable plugin is exactly the one a user is trying to remove.\n */\nexport const identify = Effect.fn(\"CLI.plugin.identify\")(function* (\n\tplugins: ReadonlyArray<unknown>,\n\tfile: string,\n\tcache: string,\n) {\n\tconst nodePath = yield* Path.Path;\n\tconst directory = nodePath.dirname(file);\n\tconst entries: Entry[] = [];\n\tfor (const value of plugins) {\n\t\tconst answers = new Set<string>();\n\t\tconst id = idOf(value);\n\t\tif (id !== undefined) answers.add(id);\n\t\tconst module = moduleOf(value);\n\t\tif (module !== undefined) {\n\t\t\tfor (const alias of yield* aliases(module, directory)) answers.add(alias);\n\t\t\tconst declared = yield* Plugin.inspect(module, {\n\t\t\t\tcache,\n\t\t\t\thostDir: directory,\n\t\t\t\tfile,\n\t\t\t\tinstall: Plugin.resolveCached,\n\t\t\t}).pipe(Effect.option);\n\t\t\tif (Option.isSome(declared)) {\n\t\t\t\tanswers.add(declared.value.id);\n\t\t\t\tif (declared.value.name !== undefined) answers.add(declared.value.name);\n\t\t\t}\n\t\t}\n\t\tentries.push({ value, answers, loads: typeof value === \"string\" });\n\t}\n\treturn entries as ReadonlyArray<Entry>;\n});\n\n/**\n * What a local reference should be *written* as, which is rarely what was typed.\n *\n * A relative path on the command line is relative to `cwd`; in a settings file it is relative to\n * that file's own directory. Those differ the moment the command runs anywhere but the directory\n * holding the file, and writing the string verbatim produces a silently wrong entry:\n *\n * ```\n * $ cd ~/workspace/app/packages/fubar\n * $ codework plugin add ./plugins/x.ts # means packages/fubar/plugins/x.ts\n * → the project is ~/workspace/app, so the entry lands in app/.codework/settings.jsonc\n * → written verbatim, the harness loads app/.codework/plugins/x.ts ← a different file\n * ```\n *\n * With both ends inside one project the entry is written relative to the file, which is portable\n * to any checkout of that repository and therefore worth committing. Otherwise -- a `-g` write\n * into `<home>`, a `--user-config-dir` elsewhere, or a target outside the project -- relative\n * would be a lie, and an absolute path means the same thing read from anywhere.\n *\n * A package spec is returned untouched: it names no location.\n */\nexport const written = Effect.fn(\"CLI.plugin.written\")(function* (\n\treference: string,\n\tinput: { readonly cwd: string; readonly file: string; readonly root: string | undefined },\n) {\n\tconst nodePath = yield* Path.Path;\n\tconst target = yield* Plugin.parse(reference, input.cwd).pipe(Effect.option);\n\tif (Option.isNone(target) || target.value.kind !== \"local\") return reference;\n\n\tconst absolute = target.value.path;\n\tconst inside = (root: string, path: string) => path === root || path.startsWith(`${root}${nodePath.sep}`);\n\tif (input.root !== undefined && inside(input.root, input.file) && inside(input.root, absolute)) {\n\t\tconst relative = nodePath.relative(nodePath.dirname(input.file), absolute);\n\t\treturn relative.startsWith(\"..\") ? relative : `./${relative}`;\n\t}\n\treturn absolute;\n});\n\n/** `reference` selects `entry` when any spelling of one is a spelling of the other. */\nexport const matches = (entry: Entry, spelled: ReadonlySet<string>) =>\n\t[...spelled].some((alias) => entry.answers.has(alias));\n\n/** Every entry connected to a reference through a package spelling or declared plugin ID. */\nexport const matching = (entries: ReadonlyArray<Entry>, spelled: ReadonlySet<string>) => {\n\tconst answers = new Set(spelled);\n\tconst found = new Set<Entry>();\n\tfor (;;) {\n\t\tconst next = entries.filter(\n\t\t\t(entry) => !found.has(entry) && [...entry.answers].some((answer) => answers.has(answer)),\n\t\t);\n\t\tif (next.length === 0) return found as ReadonlySet<Entry>;\n\t\tfor (const entry of next) {\n\t\t\tfound.add(entry);\n\t\t\tfor (const answer of entry.answers) answers.add(answer);\n\t\t}\n\t}\n};\n\nexport const readPlugins = Effect.fn(\"CLI.plugin.readPlugins\")(function* (path: string) {\n\tconst fs = yield* FileSystem.FileSystem;\n\tconst exists = yield* fs.exists(path);\n\tconst source = exists ? yield* fs.readFileString(path) : \"{}\";\n\tconst errors: ParseError[] = [];\n\tconst document: unknown = parseJsonc(source, errors, { allowTrailingComma: true });\n\tif (errors.length > 0 || typeof document !== \"object\" || document === null || Array.isArray(document)) {\n\t\treturn yield* new InvalidInputError({ message: `${path} is not a valid settings file` });\n\t}\n\tconst plugins = (document as { plugins?: unknown }).plugins;\n\tif (plugins !== undefined && !Array.isArray(plugins)) {\n\t\treturn yield* new InvalidInputError({ message: `${path}: \"plugins\" must be an array` });\n\t}\n\treturn { source, plugins: (plugins ?? []) as ReadonlyArray<unknown> };\n});\n\nexport const writePlugins = Effect.fn(\"CLI.plugin.writePlugins\")(function* (\n\tpath: string,\n\tsource: string,\n\tplugins: ReadonlyArray<unknown>,\n) {\n\tconst fs = yield* FileSystem.FileSystem;\n\tconst nodePath = yield* Path.Path;\n\tconst edited = applyEdits(\n\t\tsource,\n\t\tmodify(source, [\"plugins\"], plugins, { formattingOptions: { tabSize: 2, insertSpaces: false } }),\n\t);\n\tconst directory = nodePath.dirname(path);\n\tyield* fs.makeDirectory(directory, { recursive: true });\n\tconst mode = (yield* fs.exists(path)) ? (yield* fs.stat(path)).mode & 0o777 : 0o600;\n\tconst temporary = yield* fs.makeTempFile({ directory, prefix: `.${nodePath.basename(path)}.`, suffix: \".tmp\" });\n\tyield* fs\n\t\t.writeFileString(temporary, edited.endsWith(\"\\n\") ? edited : `${edited}\\n`)\n\t\t.pipe(\n\t\t\tEffect.andThen(fs.chmod(temporary, mode)),\n\t\t\tEffect.andThen(fs.rename(temporary, path)),\n\t\t\tEffect.ensuring(fs.remove(temporary, { force: true }).pipe(Effect.ignore)),\n\t\t);\n});\n\n/**\n * Serialize one settings read-modify-rename across CLI processes.\n *\n * The install or inspection that precedes an edit stays outside this scope; only the local edit is\n * locked, so unrelated downloads do not wait behind one another.\n */\nexport const withPluginsLock = <A, E, R>(path: string, edit: Effect.Effect<A, E, R>) =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst nodePath = yield* Path.Path;\n\t\tyield* fs.makeDirectory(nodePath.dirname(path), { recursive: true });\n\t\tyield* Plugin.settingsLock(\n\t\t\t`${path}.lock`,\n\t\t\t() => new InvalidInputError({ message: `timed out waiting to edit ${path}` }),\n\t\t);\n\t\treturn yield* edit;\n\t}).pipe(Effect.scoped);\n\n/**\n * Which file the edit lands in.\n *\n * A plugin belongs to a project by default — it is part of how that repository is worked on, so\n * the entry belongs in a file the repository can commit. `--global` writes the user-wide file\n * instead, the way `npm -g` installs outside a package. An explicit `--user-config-dir` outranks\n * both, since naming a settings directory is already a decision about where settings live.\n *\n * The project is found the way the harness finds it: the nearest ancestor holding a `.codework`\n * directory, so a command run from `packages/app` edits the repository's own file rather than\n * creating a second one beside it. When no ancestor has one, the project begins here -- the\n * directory is created and the caller is told, because that decision changes where every future\n * plugin entry lands, and a mistyped `cd` is the way it goes wrong.\n *\n * There is nothing to warn about between layers: plugin entries from every layer accumulate, so a\n * user-wide entry still applies inside a project that declares its own.\n */\nexport const resolveTarget = Effect.fn(\"CLI.plugin.resolveTarget\")(function* (\n\tshared: Shared,\n\tglobal: boolean,\n\t/**\n\t * Where to discover the project from, when it is not the shell's directory.\n\t *\n\t * A session's host directory is the only other honest answer: it is a declared host path. The\n\t * session's *space* is not, and neither is its project -- a `Project` is env-independent by\n\t * design, so it has no host directory to offer.\n\t */\n\tfrom?: string,\n\t/** Only `plugin add` may establish a project marker when no project exists yet. */\n\tcreate = true,\n) {\n\tconst fs = yield* FileSystem.FileSystem;\n\tconst nodePath = yield* Path.Path;\n\tconst cwd = from ?? nodePath.resolve(\".\");\n\tconst home = yield* Global.resolve(Option.isNone(shared.home) ? {} : { home: shared.home.value });\n\tconst root = global ? undefined : yield* Settings.projectRoot(cwd, home.home);\n\t// `paths` is ordered lowest priority first: user-wide, then project, then an explicit directory.\n\tconst groups = Settings.paths({\n\t\thome: home.home,\n\t\t...(root === undefined ? {} : { root }),\n\t\tfrom: cwd,\n\t\t...(Option.isNone(shared.userConfigDir) ? {} : { custom: shared.userConfigDir.value }),\n\t});\n\tconst chosen = Option.isSome(shared.userConfigDir) ? groups.length - 1 : global ? 0 : 1;\n\t// An empty group is the project layer saying there is no project above this directory. Then\n\t// this directory becomes one -- reported, not silent, because the marker shadows any outer\n\t// project from now on, so a `.codework/` created by a mistyped `cd` is worth spotting at once.\n\tconst found = groups[chosen] ?? [];\n\tif (found.length > 0) {\n\t\tconst existing = yield* Effect.findFirst(found, (candidate) => fs.exists(candidate));\n\t\treturn {\n\t\t\tpath: Option.getOrElse(existing, () => found[0]!),\n\t\t\t...(root === undefined ? {} : { root }),\n\t\t} satisfies Target;\n\t}\n\tconst marker = nodePath.join(cwd, \".codework\");\n\t// `created` is reported either way: with `create` unset the marker is named, not made, so a\n\t// caller can say what its write is about to establish without establishing it early.\n\tif (!create) return { path: nodePath.join(marker, \"settings.jsonc\"), created: marker, root: cwd } satisfies Target;\n\tyield* fs.makeDirectory(marker, { recursive: true });\n\treturn { path: nodePath.join(marker, \"settings.jsonc\"), created: marker, root: cwd } satisfies Target;\n});\n"],"mappings":"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"}
|