@asermax/pi-tentacles 1.0.0 → 1.2.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 +2 -2
- package/extension/index.js +49 -7
- package/package.json +1 -1
- package/skills/tentacles/SKILL.md +18 -1
- package/tentacles-helper +564 -254
package/README.md
CHANGED
|
@@ -29,10 +29,10 @@ The helper reads these where it runs, on your machine. On pi, set them in pi's s
|
|
|
29
29
|
| `TENTACLES_API_KEY` | the API key | required |
|
|
30
30
|
| `TENTACLES_HELPER` | the helper's path | the bundled helper |
|
|
31
31
|
| `TENTACLES_SESSION_PROVIDER` | the model provider | `cloudflare` |
|
|
32
|
-
| `TENTACLES_SESSION_MODEL` | the model | `glm-5.
|
|
32
|
+
| `TENTACLES_SESSION_MODEL` | the model | `glm-5.3` |
|
|
33
33
|
| `TENTACLES_SESSION_EFFORT` | the effort level | `max` |
|
|
34
34
|
| `TENTACLES_WORKSPACE_IMAGE` | the workspace image | `default` |
|
|
35
|
-
| `TENTACLES_WORKSPACE_SIZE` | the workspace container's instance type | `
|
|
35
|
+
| `TENTACLES_WORKSPACE_SIZE` | the workspace container's instance type | `standard-2` |
|
|
36
36
|
| `TENTACLES_WORKSPACE_LIFETIME` | the workspace's inactivity time, in minutes | `15` |
|
|
37
37
|
|
|
38
38
|
A setting the agent passes to `spawn` wins over its variable.
|
package/extension/index.js
CHANGED
|
@@ -15382,7 +15382,13 @@ var SessionStatuses = {
|
|
|
15382
15382
|
var SessionStatus = Enum(SessionStatuses);
|
|
15383
15383
|
var Question = Struct({
|
|
15384
15384
|
id: QuestionId,
|
|
15385
|
-
text: String$1
|
|
15385
|
+
text: String$1,
|
|
15386
|
+
repositoryCheck: Union([
|
|
15387
|
+
TaggedStruct("NoRepository", {}),
|
|
15388
|
+
TaggedStruct("Stopped", {}),
|
|
15389
|
+
TaggedStruct("Clean", {}),
|
|
15390
|
+
TaggedStruct("CheckFailed", { output: String$1 })
|
|
15391
|
+
]).pipe(toTaggedUnion("_tag"))
|
|
15386
15392
|
});
|
|
15387
15393
|
var SessionDetails = Struct({
|
|
15388
15394
|
settings: Settings,
|
|
@@ -17105,6 +17111,32 @@ var http = (options) => Object.assign(Object.create(Proto), {
|
|
|
17105
17111
|
* @since 4.0.0
|
|
17106
17112
|
*/
|
|
17107
17113
|
var bearer = /*#__PURE__*/ http({ scheme: "Bearer" });
|
|
17114
|
+
// ../../packages/worker-api/src/archive-kinds.ts
|
|
17115
|
+
/**
|
|
17116
|
+
* The kinds of archive a create request carries. `expandsInto` is where the workspace container
|
|
17117
|
+
* expands the kind's archive; a null one is read by the Worker only and never reaches the container.
|
|
17118
|
+
*/
|
|
17119
|
+
var archiveKinds = {
|
|
17120
|
+
skills: {
|
|
17121
|
+
heading: "Skills",
|
|
17122
|
+
expandsInto: "/skills"
|
|
17123
|
+
},
|
|
17124
|
+
agents: {
|
|
17125
|
+
heading: "Agents",
|
|
17126
|
+
expandsInto: null
|
|
17127
|
+
},
|
|
17128
|
+
context: {
|
|
17129
|
+
heading: "Context",
|
|
17130
|
+
expandsInto: "/context"
|
|
17131
|
+
}
|
|
17132
|
+
};
|
|
17133
|
+
keys(archiveKinds).flatMap((kind) => {
|
|
17134
|
+
const { expandsInto } = archiveKinds[kind];
|
|
17135
|
+
return expandsInto == null ? [] : [{
|
|
17136
|
+
kind,
|
|
17137
|
+
directory: expandsInto
|
|
17138
|
+
}];
|
|
17139
|
+
});
|
|
17108
17140
|
// ../../packages/worker-api/src/middleware.ts
|
|
17109
17141
|
/** Every request carries the API key as a bearer token; a provider checks it against its secret. */
|
|
17110
17142
|
var ApiKey = class extends Service()("tentacles/ApiKey", {
|
|
@@ -17121,8 +17153,7 @@ var CreateSession = Struct({
|
|
|
17121
17153
|
parent: Parent,
|
|
17122
17154
|
settings: Settings,
|
|
17123
17155
|
prompt: String$1,
|
|
17124
|
-
|
|
17125
|
-
agents: optionalKey(Archive)
|
|
17156
|
+
...map$5(archiveKinds, () => optionalKey(Archive))
|
|
17126
17157
|
});
|
|
17127
17158
|
var Prompt = Struct({
|
|
17128
17159
|
text: String$1,
|
|
@@ -17209,10 +17240,15 @@ var endpointArguments = (name) => {
|
|
|
17209
17240
|
};
|
|
17210
17241
|
/** What the agent sends to spawn: the glue fills the parent. */
|
|
17211
17242
|
var SpawnArguments = CreateSession.mapFields((fields) => ({
|
|
17212
|
-
...omit(fields, [
|
|
17243
|
+
...omit(fields, [
|
|
17244
|
+
"skills",
|
|
17245
|
+
"agents",
|
|
17246
|
+
"context"
|
|
17247
|
+
]),
|
|
17213
17248
|
settings: optionalKey(Settings.mapFields(map(optionalKey))),
|
|
17214
17249
|
skills: optionalKey(ArraySchema(String$1).annotate({ description: "Paths of skill folders on this machine, each holding a SKILL.md." })),
|
|
17215
|
-
agents: optionalKey(ArraySchema(String$1).annotate({ description: "Paths of agent definition files on this machine." }))
|
|
17250
|
+
agents: optionalKey(ArraySchema(String$1).annotate({ description: "Paths of agent definition files on this machine." })),
|
|
17251
|
+
context: optionalKey(ArraySchema(String$1).annotate({ description: "Paths of files or folders on this machine; the remote agent finds them in /context, each under its own name." }))
|
|
17216
17252
|
})).mapFields(omit(["parent"]));
|
|
17217
17253
|
// src/core/tool-table.ts
|
|
17218
17254
|
var ToolNames = {
|
|
@@ -17231,7 +17267,7 @@ var spawnDocument = toJsonSchemaDocument(SpawnArguments);
|
|
|
17231
17267
|
var ToolTable = {
|
|
17232
17268
|
spawn: ToolEntry.Endpoint({
|
|
17233
17269
|
name: ToolNames.spawn,
|
|
17234
|
-
description: "Start a remote agent with a name, a first prompt and optional settings. The settings left out come from the plugin's defaults. Optionally takes the paths of skill folders and agent definition files, which stay fixed for the session. Answers with the new session's id.",
|
|
17270
|
+
description: "Start a remote agent with a name, a first prompt and optional settings. The settings left out come from the plugin's defaults. Optionally takes the paths of skill folders and agent definition files, and the paths of context files or folders the remote agent finds in /context, which stay fixed for the session. Answers with the new session's id.",
|
|
17235
17271
|
endpoint: "create",
|
|
17236
17272
|
harnesses: bothHarnesses,
|
|
17237
17273
|
arguments: {
|
|
@@ -17293,7 +17329,13 @@ var entryText = type().pipe(tagsExhaustive({
|
|
|
17293
17329
|
toolResult: (entry) => entry.output,
|
|
17294
17330
|
subagentResult: (entry) => entry.text
|
|
17295
17331
|
}));
|
|
17296
|
-
var
|
|
17332
|
+
var repositoryCheckText = type().pipe(tagsExhaustive({
|
|
17333
|
+
NoRepository: () => "NoRepository",
|
|
17334
|
+
Stopped: () => "Stopped",
|
|
17335
|
+
Clean: () => "Clean",
|
|
17336
|
+
CheckFailed: ({ output }) => output === "" ? "CheckFailed" : `CheckFailed\n${output}`
|
|
17337
|
+
}));
|
|
17338
|
+
var questionWake = (session, question) => `${agentOf(session)} asks (question ${question.id}):\n${question.text}\n\nRepository check: ${repositoryCheckText(question.repositoryCheck)}\n\nAnswer it with the answer tool, passing session ${session.sessionId} and question ${question.id}.`;
|
|
17297
17339
|
var finishWake = (session, lastEntry) => lastEntry == null ? `${agentOf(session)} finished. Read its last message with the messages tool.` : `${agentOf(session)} finished. Its last message:\n${entryText(lastEntry)}`;
|
|
17298
17340
|
var failureWake = (session, failure) => `${agentOf(session)} failed: ${failure}`;
|
|
17299
17341
|
// src/core/controller.ts
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@ A remote agent only keeps what it commits and pushes. Its workspace is disposabl
|
|
|
16
16
|
|
|
17
17
|
## Spawn
|
|
18
18
|
|
|
19
|
-
`spawn` takes a name, the first prompt, the session's settings and, optionally, skills
|
|
19
|
+
`spawn` takes a name, the first prompt, the session's settings and, optionally, skills, agent definitions and context files (see Skills and agents, and Context files). The name shows in your status line, so name it after the agent's task. The settings you leave out come from the plugin's defaults.
|
|
20
20
|
|
|
21
21
|
Write the first prompt for someone who has not seen this conversation: the goal, the repository, the branch to push, and what done looks like. Name the repository in the settings when the agent has to work in it, so tentacles clones the repository and, before the agent asks you anything, checks it for work that is not pushed.
|
|
22
22
|
|
|
@@ -30,6 +30,14 @@ Every definition is checked on your machine before anything is sent. When any fa
|
|
|
30
30
|
|
|
31
31
|
A remote agent works on its own, so a skill that would stop to ask the person has nobody to ask. Write the handoff so the remote agent decides wherever a skill would stop for the user, ignores any instruction in a skill to ask the user, and lists every such decision in its final report. For a stop point it cannot decide, such as a credential it lacks, say in the handoff what to do, which can be asking you with its question tool.
|
|
32
32
|
|
|
33
|
+
## Context files
|
|
34
|
+
|
|
35
|
+
`spawn` takes `context`, the paths of files and folders on your machine that the job needs and the repository does not hold, such as a change's scratch files. Each path lands in the remote agent's `/context` folder under its own name: a file at `/context/<file name>`, a folder at `/context/<folder name>/…`. `/context` is outside the repository, so the files never reach its commits. A folder takes everything under it, a symlink is sent as the file or folder it points to, and every file keeps its permission bits.
|
|
36
|
+
|
|
37
|
+
The remote agent has a copy that is fixed at spawn. Its edits to the copy are lost when its workspace restarts and never come back to you, so spawn a new remote agent when the job needs different files. The remote agent is told only that you sent files and that they are in `/context`, so say in the handoff what the files are and how to use them.
|
|
38
|
+
|
|
39
|
+
The paths are checked on your machine before anything is sent. A path that does not exist or cannot be read, two paths with the same name, or files that pack over 5 MB make `spawn` create nothing and return every problem with its path in a Context group, beside the Skills and Agents groups.
|
|
40
|
+
|
|
33
41
|
## Being woken
|
|
34
42
|
|
|
35
43
|
You are woken when a remote agent stops. The wake carries the agent's last message when it finishes, the reason when it fails, and the question when it asks one. You never poll. If you think a wake was missed, run `list`.
|
|
@@ -38,6 +46,15 @@ You are woken when a remote agent stops. The wake carries the agent's last messa
|
|
|
38
46
|
|
|
39
47
|
A remote agent asks with its question tool and waits for the answer. Answer with `answer`, passing the question's id. Answer it yourself when you can; when the decision is the human's, ask them and answer once they reply. A prompt you send while a question waits does not answer the question. The agent receives the prompt after the question is answered.
|
|
40
48
|
|
|
49
|
+
Every question comes with a repository check, which tells you whether the remote agent's work is known to be persisted:
|
|
50
|
+
|
|
51
|
+
- `Clean`: the work is committed and pushed. The question can wait any length of time.
|
|
52
|
+
- `Stopped`: the agent's workspace was not running, so it had nothing pending. The question can wait any length of time.
|
|
53
|
+
- `CheckFailed`: tentacles could not check the workspace, so the agent's work may exist only on the container's disk and is lost when the container stops. The check comes with what the git commands printed, which shows why it failed and is empty when the workspace ran neither command.
|
|
54
|
+
- `NoRepository`: the session's settings name no repository, so tentacles manages none and checked nothing.
|
|
55
|
+
|
|
56
|
+
A remote agent that has uncommitted changes or unpushed commits cannot ask: its question tool refuses until it commits and pushes.
|
|
57
|
+
|
|
41
58
|
## Prompt
|
|
42
59
|
|
|
43
60
|
`prompt` sends a follow-up or a steer. A follow-up waits for the current run to end; a steer reaches the agent at its next step. Prompting a closed session reopens it.
|
package/tentacles-helper
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
3
|
+
import * as fsp from "node:fs/promises";
|
|
4
|
+
import { readFile, realpath, stat } from "node:fs/promises";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import { basename, join, resolve } from "node:path";
|
|
7
|
+
import { pipeline } from "node:stream/promises";
|
|
8
|
+
import { createGzip } from "node:zlib";
|
|
9
|
+
import * as fs from "node:fs";
|
|
10
|
+
import { cpus } from "node:os";
|
|
11
|
+
import { Readable } from "node:stream";
|
|
5
12
|
// \0rolldown/runtime.js
|
|
6
13
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod, mod.exports), cb = null), mod.exports);
|
|
7
14
|
var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
|
|
@@ -26240,7 +26247,13 @@ var SessionStatuses = {
|
|
|
26240
26247
|
var SessionStatus = Enum(SessionStatuses);
|
|
26241
26248
|
var Question = Struct({
|
|
26242
26249
|
id: QuestionId,
|
|
26243
|
-
text: String$2
|
|
26250
|
+
text: String$2,
|
|
26251
|
+
repositoryCheck: Union([
|
|
26252
|
+
TaggedStruct("NoRepository", {}),
|
|
26253
|
+
TaggedStruct("Stopped", {}),
|
|
26254
|
+
TaggedStruct("Clean", {}),
|
|
26255
|
+
TaggedStruct("CheckFailed", { output: String$2 })
|
|
26256
|
+
]).pipe(toTaggedUnion("_tag"))
|
|
26244
26257
|
});
|
|
26245
26258
|
var SessionDetails = Struct({
|
|
26246
26259
|
settings: Settings,
|
|
@@ -29256,6 +29269,34 @@ var http = (options) => Object.assign(Object.create(Proto$2), {
|
|
|
29256
29269
|
* @since 4.0.0
|
|
29257
29270
|
*/
|
|
29258
29271
|
var bearer = /*#__PURE__*/ http({ scheme: "Bearer" });
|
|
29272
|
+
// ../../packages/worker-api/src/archive-kinds.ts
|
|
29273
|
+
/**
|
|
29274
|
+
* The kinds of archive a create request carries. `expandsInto` is where the workspace container
|
|
29275
|
+
* expands the kind's archive; a null one is read by the Worker only and never reaches the container.
|
|
29276
|
+
*/
|
|
29277
|
+
var archiveKinds = {
|
|
29278
|
+
skills: {
|
|
29279
|
+
heading: "Skills",
|
|
29280
|
+
expandsInto: "/skills"
|
|
29281
|
+
},
|
|
29282
|
+
agents: {
|
|
29283
|
+
heading: "Agents",
|
|
29284
|
+
expandsInto: null
|
|
29285
|
+
},
|
|
29286
|
+
context: {
|
|
29287
|
+
heading: "Context",
|
|
29288
|
+
expandsInto: "/context"
|
|
29289
|
+
}
|
|
29290
|
+
};
|
|
29291
|
+
/** The kinds in table order. */
|
|
29292
|
+
var archiveKindNames = keys(archiveKinds);
|
|
29293
|
+
archiveKindNames.flatMap((kind) => {
|
|
29294
|
+
const { expandsInto } = archiveKinds[kind];
|
|
29295
|
+
return expandsInto == null ? [] : [{
|
|
29296
|
+
kind,
|
|
29297
|
+
directory: expandsInto
|
|
29298
|
+
}];
|
|
29299
|
+
});
|
|
29259
29300
|
// ../../packages/worker-api/src/middleware.ts
|
|
29260
29301
|
/** Every request carries the API key as a bearer token; a provider checks it against its secret. */
|
|
29261
29302
|
var ApiKey = class extends Service()("tentacles/ApiKey", {
|
|
@@ -29280,8 +29321,7 @@ var CreateSession = Struct({
|
|
|
29280
29321
|
parent: Parent,
|
|
29281
29322
|
settings: Settings,
|
|
29282
29323
|
prompt: String$2,
|
|
29283
|
-
|
|
29284
|
-
agents: optionalKey(Archive)
|
|
29324
|
+
...map$8(archiveKinds, () => optionalKey(Archive))
|
|
29285
29325
|
});
|
|
29286
29326
|
var Prompt = Struct({
|
|
29287
29327
|
text: String$2,
|
|
@@ -30157,10 +30197,10 @@ var ConnectionConfig = all({
|
|
|
30157
30197
|
/** The settings a spawn leaves out. */
|
|
30158
30198
|
var SettingsDefaults = all({
|
|
30159
30199
|
provider: String$1("TENTACLES_SESSION_PROVIDER").pipe(withDefault("cloudflare")),
|
|
30160
|
-
model: String$1("TENTACLES_SESSION_MODEL").pipe(withDefault("glm-5.
|
|
30200
|
+
model: String$1("TENTACLES_SESSION_MODEL").pipe(withDefault("glm-5.3")),
|
|
30161
30201
|
effort: String$1("TENTACLES_SESSION_EFFORT").pipe(withDefault("max")),
|
|
30162
30202
|
image: String$1("TENTACLES_WORKSPACE_IMAGE").pipe(withDefault("default")),
|
|
30163
|
-
size: String$1("TENTACLES_WORKSPACE_SIZE").pipe(withDefault("
|
|
30203
|
+
size: String$1("TENTACLES_WORKSPACE_SIZE").pipe(withDefault("standard-2")),
|
|
30164
30204
|
lifetime: Int("TENTACLES_WORKSPACE_LIFETIME").pipe(withDefault(15))
|
|
30165
30205
|
});
|
|
30166
30206
|
// src/helper/client.ts
|
|
@@ -30476,16 +30516,25 @@ var exhaustive = exhaustive$1;
|
|
|
30476
30516
|
/**
|
|
30477
30517
|
* What the helper receives for spawn: CreateSession with every setting
|
|
30478
30518
|
* optional, since the helper fills the rest from its variables, and with the
|
|
30479
|
-
* paths of the skills and
|
|
30519
|
+
* paths of the skills, agents and context in place of their archives, which the helper packs.
|
|
30480
30520
|
*/
|
|
30481
30521
|
var SpawnRequest = CreateSession.mapFields((fields) => ({
|
|
30482
|
-
...omit(fields, [
|
|
30522
|
+
...omit(fields, [
|
|
30523
|
+
"skills",
|
|
30524
|
+
"agents",
|
|
30525
|
+
"context"
|
|
30526
|
+
]),
|
|
30483
30527
|
settings: optionalKey(Settings.mapFields(map$3(optionalKey))),
|
|
30484
30528
|
skills: optionalKey(ArraySchema(String$2).annotate({ description: "Paths of skill folders on this machine, each holding a SKILL.md." })),
|
|
30485
|
-
agents: optionalKey(ArraySchema(String$2).annotate({ description: "Paths of agent definition files on this machine." }))
|
|
30529
|
+
agents: optionalKey(ArraySchema(String$2).annotate({ description: "Paths of agent definition files on this machine." })),
|
|
30530
|
+
context: optionalKey(ArraySchema(String$2).annotate({ description: "Paths of files or folders on this machine; the remote agent finds them in /context, each under its own name." }))
|
|
30486
30531
|
}));
|
|
30487
30532
|
SpawnRequest.mapFields(omit(["parent"]));
|
|
30488
30533
|
// ../../node_modules/.pnpm/modern-tar@0.8.6/node_modules/modern-tar/dist/packer-By1HVV7Z.js
|
|
30534
|
+
var FILE = "file";
|
|
30535
|
+
var LINK = "link";
|
|
30536
|
+
var SYMLINK = "symlink";
|
|
30537
|
+
var DIRECTORY = "directory";
|
|
30489
30538
|
var TYPEFLAG = {
|
|
30490
30539
|
file: "0",
|
|
30491
30540
|
link: "1",
|
|
@@ -30645,7 +30694,7 @@ function getHeaderBlocks(header) {
|
|
|
30645
30694
|
];
|
|
30646
30695
|
}
|
|
30647
30696
|
var EOF_BUFFER = /* @__PURE__ */ new Uint8Array(1024);
|
|
30648
|
-
function createTarPacker
|
|
30697
|
+
function createTarPacker(onData) {
|
|
30649
30698
|
let currentHeader = null;
|
|
30650
30699
|
let bytesWritten = 0;
|
|
30651
30700
|
let finalized = false;
|
|
@@ -30694,171 +30743,6 @@ function createTarPacker$1(onData) {
|
|
|
30694
30743
|
}
|
|
30695
30744
|
};
|
|
30696
30745
|
}
|
|
30697
|
-
// ../../node_modules/.pnpm/modern-tar@0.8.6/node_modules/modern-tar/dist/web/index.js
|
|
30698
|
-
function createTarPacker() {
|
|
30699
|
-
let streamController;
|
|
30700
|
-
let packer;
|
|
30701
|
-
let bodyController = null;
|
|
30702
|
-
let resume = null;
|
|
30703
|
-
let drain = null;
|
|
30704
|
-
let stopped = false;
|
|
30705
|
-
let stopReason;
|
|
30706
|
-
const unblock = () => {
|
|
30707
|
-
resume?.();
|
|
30708
|
-
resume = null;
|
|
30709
|
-
drain = null;
|
|
30710
|
-
};
|
|
30711
|
-
const stop = (reason) => {
|
|
30712
|
-
if (stopped) return;
|
|
30713
|
-
stopped = true;
|
|
30714
|
-
stopReason = reason;
|
|
30715
|
-
bodyController?.error(reason);
|
|
30716
|
-
bodyController = null;
|
|
30717
|
-
streamController.error(reason);
|
|
30718
|
-
unblock();
|
|
30719
|
-
};
|
|
30720
|
-
const abortBody = (reason = AbortSignal.abort().reason) => {
|
|
30721
|
-
bodyController = null;
|
|
30722
|
-
stop(reason);
|
|
30723
|
-
};
|
|
30724
|
-
const checkStopped = () => {
|
|
30725
|
-
if (stopped) throw stopReason;
|
|
30726
|
-
};
|
|
30727
|
-
const emit = (operation) => {
|
|
30728
|
-
checkStopped();
|
|
30729
|
-
try {
|
|
30730
|
-
operation();
|
|
30731
|
-
} catch (error) {
|
|
30732
|
-
stop(error);
|
|
30733
|
-
throw error;
|
|
30734
|
-
}
|
|
30735
|
-
const pending = drain;
|
|
30736
|
-
if (!pending) return;
|
|
30737
|
-
const signal = bodyController?.signal;
|
|
30738
|
-
if (!signal) return;
|
|
30739
|
-
if (signal.aborted) abortBody(signal.reason);
|
|
30740
|
-
else signal.onabort = () => abortBody(signal.reason);
|
|
30741
|
-
return pending.then(checkStopped);
|
|
30742
|
-
};
|
|
30743
|
-
return {
|
|
30744
|
-
readable: new ReadableStream({
|
|
30745
|
-
start(controller) {
|
|
30746
|
-
streamController = controller;
|
|
30747
|
-
packer = createTarPacker$1((chunk) => {
|
|
30748
|
-
const buffer = chunk.buffer;
|
|
30749
|
-
controller.enqueue(buffer instanceof ArrayBuffer && !Reflect.get(buffer, "resizable") ? chunk : new Uint8Array(chunk));
|
|
30750
|
-
if ((controller.desiredSize ?? 1) <= 0) drain ||= new Promise((resolve) => {
|
|
30751
|
-
resume = resolve;
|
|
30752
|
-
});
|
|
30753
|
-
});
|
|
30754
|
-
},
|
|
30755
|
-
pull: unblock,
|
|
30756
|
-
cancel(reason = AbortSignal.abort().reason) {
|
|
30757
|
-
stop(reason);
|
|
30758
|
-
}
|
|
30759
|
-
}, new ByteLengthQueuingStrategy({ highWaterMark: 8388608 })),
|
|
30760
|
-
controller: {
|
|
30761
|
-
add(header) {
|
|
30762
|
-
try {
|
|
30763
|
-
checkStopped();
|
|
30764
|
-
if (bodyController) throw new Error("Previous entry must be completed before adding a new one");
|
|
30765
|
-
packer.add(header);
|
|
30766
|
-
} catch (error) {
|
|
30767
|
-
stop(error);
|
|
30768
|
-
throw error;
|
|
30769
|
-
}
|
|
30770
|
-
return new WritableStream({
|
|
30771
|
-
start(controller) {
|
|
30772
|
-
bodyController = controller;
|
|
30773
|
-
},
|
|
30774
|
-
write(chunk) {
|
|
30775
|
-
return emit(() => {
|
|
30776
|
-
if (!ArrayBuffer.isView(chunk) || chunk[Symbol.toStringTag] !== "Uint8Array") throw new TypeError("Expected a Uint8Array body chunk");
|
|
30777
|
-
packer.write(chunk);
|
|
30778
|
-
});
|
|
30779
|
-
},
|
|
30780
|
-
close() {
|
|
30781
|
-
const result = emit(() => packer.endEntry());
|
|
30782
|
-
if (!result) {
|
|
30783
|
-
bodyController = null;
|
|
30784
|
-
return;
|
|
30785
|
-
}
|
|
30786
|
-
return result.then(() => {
|
|
30787
|
-
bodyController = null;
|
|
30788
|
-
});
|
|
30789
|
-
},
|
|
30790
|
-
abort: abortBody
|
|
30791
|
-
});
|
|
30792
|
-
},
|
|
30793
|
-
finalize() {
|
|
30794
|
-
try {
|
|
30795
|
-
checkStopped();
|
|
30796
|
-
if (bodyController) throw new Error("Cannot finalize while an entry is still active");
|
|
30797
|
-
packer.finalize();
|
|
30798
|
-
streamController.close();
|
|
30799
|
-
} catch (error) {
|
|
30800
|
-
stop(error);
|
|
30801
|
-
throw error;
|
|
30802
|
-
}
|
|
30803
|
-
},
|
|
30804
|
-
error(err) {
|
|
30805
|
-
stop(err);
|
|
30806
|
-
}
|
|
30807
|
-
}
|
|
30808
|
-
};
|
|
30809
|
-
}
|
|
30810
|
-
async function streamToBuffer(stream) {
|
|
30811
|
-
const chunks = [];
|
|
30812
|
-
const reader = stream.getReader();
|
|
30813
|
-
let totalLength = 0;
|
|
30814
|
-
try {
|
|
30815
|
-
while (true) {
|
|
30816
|
-
const { done, value } = await reader.read();
|
|
30817
|
-
if (done) break;
|
|
30818
|
-
chunks.push(value);
|
|
30819
|
-
totalLength += value.length;
|
|
30820
|
-
}
|
|
30821
|
-
const result = new Uint8Array(totalLength);
|
|
30822
|
-
let offset = 0;
|
|
30823
|
-
for (const chunk of chunks) {
|
|
30824
|
-
result.set(chunk, offset);
|
|
30825
|
-
offset += chunk.length;
|
|
30826
|
-
}
|
|
30827
|
-
return result;
|
|
30828
|
-
} finally {
|
|
30829
|
-
reader.releaseLock();
|
|
30830
|
-
}
|
|
30831
|
-
}
|
|
30832
|
-
async function packTar(entries) {
|
|
30833
|
-
const { readable, controller } = createTarPacker();
|
|
30834
|
-
const archive = streamToBuffer(readable);
|
|
30835
|
-
try {
|
|
30836
|
-
for (const entry of entries) {
|
|
30837
|
-
const entryStream = controller.add(entry.header);
|
|
30838
|
-
const body = "body" in entry ? entry.body : entry.data;
|
|
30839
|
-
if (!body) {
|
|
30840
|
-
await entryStream.close();
|
|
30841
|
-
continue;
|
|
30842
|
-
}
|
|
30843
|
-
if (body instanceof ReadableStream) await body.pipeTo(entryStream);
|
|
30844
|
-
else if (body instanceof Blob) await body.stream().pipeTo(entryStream);
|
|
30845
|
-
else try {
|
|
30846
|
-
const chunk = await normalizeBody(body);
|
|
30847
|
-
if (chunk.length > 0) {
|
|
30848
|
-
const writer = entryStream.getWriter();
|
|
30849
|
-
await writer.write(chunk);
|
|
30850
|
-
await writer.close();
|
|
30851
|
-
} else await entryStream.close();
|
|
30852
|
-
} catch {
|
|
30853
|
-
throw new TypeError(`Unsupported content type for entry "${entry.header.name}".`);
|
|
30854
|
-
}
|
|
30855
|
-
}
|
|
30856
|
-
controller.finalize();
|
|
30857
|
-
} catch (error) {
|
|
30858
|
-
controller.error(error);
|
|
30859
|
-
}
|
|
30860
|
-
return archive;
|
|
30861
|
-
}
|
|
30862
30746
|
// ../../node_modules/.pnpm/yaml@2.9.1/node_modules/yaml/dist/nodes/identity.js
|
|
30863
30747
|
var require_identity = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
30864
30748
|
var ALIAS = Symbol.for("yaml.alias");
|
|
@@ -37575,34 +37459,410 @@ var checkAgents = (sources) => {
|
|
|
37575
37459
|
};
|
|
37576
37460
|
TaggedError()("ArchiveUnreadable", { message: String$2 });
|
|
37577
37461
|
var skillFile = "SKILL.md";
|
|
37578
|
-
var gzip = async (bytes) => new Uint8Array(await new Response(new Blob([bytes]).stream().pipeThrough(new CompressionStream("gzip"))).arrayBuffer());
|
|
37579
|
-
/** Packs entries into a tar.gz, the form `readArchive` reads. */
|
|
37580
|
-
var packArchive = (entries) => promise(async () => gzip(await packTar(entries.map((entry) => ({
|
|
37581
|
-
header: {
|
|
37582
|
-
name: entry.path,
|
|
37583
|
-
size: entry.bytes.byteLength,
|
|
37584
|
-
mode: entry.mode
|
|
37585
|
-
},
|
|
37586
|
-
body: entry.bytes
|
|
37587
|
-
})))));
|
|
37588
37462
|
new TextDecoder();
|
|
37463
|
+
// ../../node_modules/.pnpm/modern-tar@0.8.6/node_modules/modern-tar/dist/fs/index.js
|
|
37464
|
+
var win32Reserved = {
|
|
37465
|
+
":": "",
|
|
37466
|
+
"<": "",
|
|
37467
|
+
">": "",
|
|
37468
|
+
"|": "",
|
|
37469
|
+
"?": "",
|
|
37470
|
+
"*": "",
|
|
37471
|
+
"\"": ""
|
|
37472
|
+
};
|
|
37473
|
+
function normalizeName(name) {
|
|
37474
|
+
const path = name.replace(/\\/g, "/");
|
|
37475
|
+
if (path.split("/").includes("..") || /^[a-zA-Z]:\.\./.test(path)) throw new Error(`${name} points outside extraction directory`);
|
|
37476
|
+
let relative = path;
|
|
37477
|
+
if (/^[a-zA-Z]:/.test(relative)) relative = relative.replace(/^[a-zA-Z]:[/\\]?/, "");
|
|
37478
|
+
else if (relative.startsWith("/")) relative = relative.replace(/^\/+/, "");
|
|
37479
|
+
if (process.platform === "win32") return relative.replace(/[<>:"|?*]/g, (char) => win32Reserved[char]);
|
|
37480
|
+
return relative;
|
|
37481
|
+
}
|
|
37482
|
+
var BIGINT_STAT = { bigint: true };
|
|
37483
|
+
var WITH_FILE_TYPES = { withFileTypes: true };
|
|
37484
|
+
function packTar(sources, options = {}) {
|
|
37485
|
+
const results = /* @__PURE__ */ new Map();
|
|
37486
|
+
const fileHandles = /* @__PURE__ */ new Map();
|
|
37487
|
+
const bodyStreams = /* @__PURE__ */ new Set();
|
|
37488
|
+
let resume = null;
|
|
37489
|
+
let drain = null;
|
|
37490
|
+
let resumeWriter = null;
|
|
37491
|
+
let cancelError;
|
|
37492
|
+
const unblock = () => {
|
|
37493
|
+
const resolve = resume;
|
|
37494
|
+
resume = null;
|
|
37495
|
+
drain = null;
|
|
37496
|
+
resolve?.();
|
|
37497
|
+
};
|
|
37498
|
+
const wakeWriter = () => {
|
|
37499
|
+
resumeWriter?.();
|
|
37500
|
+
resumeWriter = null;
|
|
37501
|
+
};
|
|
37502
|
+
const destroyBody = (body, reason) => {
|
|
37503
|
+
bodyStreams.delete(body);
|
|
37504
|
+
body.destroy(reason);
|
|
37505
|
+
};
|
|
37506
|
+
const closeHandle = (handle) => {
|
|
37507
|
+
const closing = fileHandles.get(handle);
|
|
37508
|
+
if (closing !== null) return closing;
|
|
37509
|
+
const promise = handle.close().finally(() => fileHandles.delete(handle));
|
|
37510
|
+
fileHandles.set(handle, promise);
|
|
37511
|
+
return promise;
|
|
37512
|
+
};
|
|
37513
|
+
const stop = async (reason) => {
|
|
37514
|
+
for (const body of bodyStreams) destroyBody(body, reason);
|
|
37515
|
+
const closing = Promise.allSettled([...fileHandles.keys()].map(closeHandle));
|
|
37516
|
+
results.clear();
|
|
37517
|
+
wakeWriter();
|
|
37518
|
+
for (const result of await closing) if (result.status === "rejected") throw result.reason;
|
|
37519
|
+
};
|
|
37520
|
+
const stream = new Readable({
|
|
37521
|
+
highWaterMark: 8388608,
|
|
37522
|
+
read: unblock,
|
|
37523
|
+
destroy(error, callback) {
|
|
37524
|
+
cancelError = error ?? AbortSignal.abort().reason;
|
|
37525
|
+
unblock();
|
|
37526
|
+
stop(cancelError).then(() => callback(error), (closeError) => callback(error ?? closeError));
|
|
37527
|
+
}
|
|
37528
|
+
});
|
|
37529
|
+
const onError = (error) => stream.destroy(error);
|
|
37530
|
+
const packer = createTarPacker((chunk) => {
|
|
37531
|
+
if (stream.destroyed) throw cancelError;
|
|
37532
|
+
if (!stream.push(Buffer.from(chunk)) && !drain) drain = new Promise((resolve) => {
|
|
37533
|
+
resume = resolve;
|
|
37534
|
+
});
|
|
37535
|
+
});
|
|
37536
|
+
(async () => {
|
|
37537
|
+
const { dereference = false, filter, map, baseDir, concurrency = cpus().length || 8 } = options;
|
|
37538
|
+
let directoryPath;
|
|
37539
|
+
let realBaseDir;
|
|
37540
|
+
let jobs;
|
|
37541
|
+
if (typeof sources === "string") {
|
|
37542
|
+
const source = path.resolve(sources);
|
|
37543
|
+
directoryPath = source;
|
|
37544
|
+
const before = await fsp.stat(source, BIGINT_STAT);
|
|
37545
|
+
if (stream.destroyed) return;
|
|
37546
|
+
const entries = await fsp.readdir(source, WITH_FILE_TYPES);
|
|
37547
|
+
if (stream.destroyed) return;
|
|
37548
|
+
const after = await fsp.stat(source, BIGINT_STAT);
|
|
37549
|
+
if (stream.destroyed) return;
|
|
37550
|
+
jobs = before.dev === after.dev && before.ino === after.ino ? entries.map((entry) => ({
|
|
37551
|
+
type: entry.isDirectory() ? DIRECTORY : FILE,
|
|
37552
|
+
source: path.join(source, entry.name),
|
|
37553
|
+
target: entry.name
|
|
37554
|
+
})) : [];
|
|
37555
|
+
} else jobs = sources.map((source) => ({ ...source }));
|
|
37556
|
+
const seenHardlinks = /* @__PURE__ */ new Map();
|
|
37557
|
+
let jobIndex = 0;
|
|
37558
|
+
let writeIndex = 0;
|
|
37559
|
+
let activeWorkers = 0;
|
|
37560
|
+
let allJobsQueued = false;
|
|
37561
|
+
const writeStreamBody = async (body) => {
|
|
37562
|
+
try {
|
|
37563
|
+
for await (const chunk of body) {
|
|
37564
|
+
if (stream.destroyed) return;
|
|
37565
|
+
packer.write(chunk instanceof Uint8Array ? chunk : Buffer.from(chunk));
|
|
37566
|
+
if (drain) await drain;
|
|
37567
|
+
}
|
|
37568
|
+
} finally {
|
|
37569
|
+
body.off("error", onError);
|
|
37570
|
+
bodyStreams.delete(body);
|
|
37571
|
+
}
|
|
37572
|
+
};
|
|
37573
|
+
const writer = async () => {
|
|
37574
|
+
const readBufferSmall = Buffer.alloc(65536);
|
|
37575
|
+
let readBufferLarge = null;
|
|
37576
|
+
while (true) {
|
|
37577
|
+
if (stream.destroyed) return;
|
|
37578
|
+
if (allJobsQueued && writeIndex >= jobs.length) break;
|
|
37579
|
+
if (!results.has(writeIndex)) {
|
|
37580
|
+
await new Promise((resolve) => {
|
|
37581
|
+
resumeWriter = resolve;
|
|
37582
|
+
});
|
|
37583
|
+
continue;
|
|
37584
|
+
}
|
|
37585
|
+
const result = results.get(writeIndex);
|
|
37586
|
+
results.delete(writeIndex);
|
|
37587
|
+
if (!result) {
|
|
37588
|
+
writeIndex++;
|
|
37589
|
+
controller();
|
|
37590
|
+
continue;
|
|
37591
|
+
}
|
|
37592
|
+
if (result.hardlinkId) {
|
|
37593
|
+
const originalName = result.header.name;
|
|
37594
|
+
const hardlinkTarget = seenHardlinks.get(result.hardlinkId);
|
|
37595
|
+
if (hardlinkTarget) {
|
|
37596
|
+
if (result.body && !(result.body instanceof Uint8Array) && !(result.body instanceof Readable)) await closeHandle(result.body.handle);
|
|
37597
|
+
result.body = void 0;
|
|
37598
|
+
result.header.type = LINK;
|
|
37599
|
+
result.header.linkname = hardlinkTarget.originalName;
|
|
37600
|
+
result.header.size = 0;
|
|
37601
|
+
}
|
|
37602
|
+
if (map) result.header = map(result.header);
|
|
37603
|
+
if (hardlinkTarget) {
|
|
37604
|
+
if (result.header.linkname === hardlinkTarget.originalName) result.header.linkname = hardlinkTarget.mappedName;
|
|
37605
|
+
} else seenHardlinks.set(result.hardlinkId, {
|
|
37606
|
+
originalName,
|
|
37607
|
+
mappedName: result.header.name
|
|
37608
|
+
});
|
|
37609
|
+
}
|
|
37610
|
+
packer.add(result.header);
|
|
37611
|
+
if (drain) await drain;
|
|
37612
|
+
if (stream.destroyed) return;
|
|
37613
|
+
if (result.body) if (result.body instanceof Uint8Array) {
|
|
37614
|
+
if (result.body.length > 0) {
|
|
37615
|
+
packer.write(result.body);
|
|
37616
|
+
if (drain) await drain;
|
|
37617
|
+
}
|
|
37618
|
+
} else if (result.body instanceof Readable) await writeStreamBody(result.body);
|
|
37619
|
+
else {
|
|
37620
|
+
const { handle, size } = result.body;
|
|
37621
|
+
const readBuffer = size > 1048576 ? readBufferLarge ??= Buffer.alloc(1048576) : readBufferSmall;
|
|
37622
|
+
try {
|
|
37623
|
+
let bytesLeft = size;
|
|
37624
|
+
while (bytesLeft > 0 && !stream.destroyed) {
|
|
37625
|
+
const { bytesRead } = await handle.read(readBuffer, 0, Math.min(bytesLeft, readBuffer.length), null);
|
|
37626
|
+
if (bytesRead === 0) break;
|
|
37627
|
+
packer.write(readBuffer.subarray(0, bytesRead));
|
|
37628
|
+
bytesLeft -= bytesRead;
|
|
37629
|
+
if (drain) await drain;
|
|
37630
|
+
}
|
|
37631
|
+
} finally {
|
|
37632
|
+
await closeHandle(handle);
|
|
37633
|
+
}
|
|
37634
|
+
}
|
|
37635
|
+
if (stream.destroyed) return;
|
|
37636
|
+
packer.endEntry();
|
|
37637
|
+
if (drain) await drain;
|
|
37638
|
+
writeIndex++;
|
|
37639
|
+
controller();
|
|
37640
|
+
}
|
|
37641
|
+
};
|
|
37642
|
+
const controller = () => {
|
|
37643
|
+
if (stream.destroyed || allJobsQueued) return;
|
|
37644
|
+
while (activeWorkers < concurrency && jobIndex < jobs.length && jobIndex - writeIndex < concurrency) {
|
|
37645
|
+
activeWorkers++;
|
|
37646
|
+
const currentIndex = jobIndex++;
|
|
37647
|
+
processJob(jobs[currentIndex], currentIndex).catch(onError).finally(() => {
|
|
37648
|
+
activeWorkers--;
|
|
37649
|
+
controller();
|
|
37650
|
+
});
|
|
37651
|
+
}
|
|
37652
|
+
if (activeWorkers === 0 && jobIndex >= jobs.length) {
|
|
37653
|
+
allJobsQueued = true;
|
|
37654
|
+
wakeWriter();
|
|
37655
|
+
}
|
|
37656
|
+
};
|
|
37657
|
+
const processJob = async (job, index) => {
|
|
37658
|
+
let jobResult = null;
|
|
37659
|
+
const target = normalizeName(job.target);
|
|
37660
|
+
try {
|
|
37661
|
+
if (job.type === "content" || job.type === "stream") {
|
|
37662
|
+
let body;
|
|
37663
|
+
let size;
|
|
37664
|
+
const isDir = target.endsWith("/");
|
|
37665
|
+
if (job.type === "stream") {
|
|
37666
|
+
if (!isDir && job.size <= 0 || isDir && job.size !== 0) throw new Error(isDir ? "Streams for directories must have size 0." : "Streams require a positive size.");
|
|
37667
|
+
size = job.size;
|
|
37668
|
+
} else {
|
|
37669
|
+
const content = await normalizeBody(job.content);
|
|
37670
|
+
size = content.length;
|
|
37671
|
+
body = content;
|
|
37672
|
+
}
|
|
37673
|
+
const stat = {
|
|
37674
|
+
size: isDir ? 0 : size,
|
|
37675
|
+
isFile: () => !isDir,
|
|
37676
|
+
isDirectory: () => isDir,
|
|
37677
|
+
isSymbolicLink: () => false,
|
|
37678
|
+
mode: job.mode,
|
|
37679
|
+
mtime: job.mtime ?? /* @__PURE__ */ new Date(),
|
|
37680
|
+
uid: job.uid ?? 0,
|
|
37681
|
+
gid: job.gid ?? 0
|
|
37682
|
+
};
|
|
37683
|
+
if (stream.destroyed) return;
|
|
37684
|
+
if (filter && !filter(target, stat)) return;
|
|
37685
|
+
if (stream.destroyed) return;
|
|
37686
|
+
let header = {
|
|
37687
|
+
name: target,
|
|
37688
|
+
type: isDir ? DIRECTORY : FILE,
|
|
37689
|
+
size: isDir ? 0 : size,
|
|
37690
|
+
mode: stat.mode,
|
|
37691
|
+
mtime: stat.mtime,
|
|
37692
|
+
uid: stat.uid,
|
|
37693
|
+
gid: stat.gid,
|
|
37694
|
+
uname: job.uname,
|
|
37695
|
+
gname: job.gname
|
|
37696
|
+
};
|
|
37697
|
+
if (map) header = map(header);
|
|
37698
|
+
if (stream.destroyed) return;
|
|
37699
|
+
if (!isDir && job.type === "stream") {
|
|
37700
|
+
body = job.content instanceof Readable ? job.content : Readable.fromWeb(job.content);
|
|
37701
|
+
body.once("error", onError);
|
|
37702
|
+
bodyStreams.add(body);
|
|
37703
|
+
}
|
|
37704
|
+
jobResult = {
|
|
37705
|
+
header,
|
|
37706
|
+
body: isDir ? void 0 : body
|
|
37707
|
+
};
|
|
37708
|
+
return;
|
|
37709
|
+
}
|
|
37710
|
+
let source = job.source;
|
|
37711
|
+
let stat = await fsp.lstat(source, BIGINT_STAT);
|
|
37712
|
+
if (stream.destroyed) return;
|
|
37713
|
+
if (dereference && (baseDir !== void 0 || stat.isSymbolicLink())) {
|
|
37714
|
+
const resolvedSource = await fsp.realpath(source);
|
|
37715
|
+
if (stream.destroyed) return;
|
|
37716
|
+
realBaseDir ??= await fsp.realpath(baseDir ?? directoryPath ?? process.cwd());
|
|
37717
|
+
if (stream.destroyed) return;
|
|
37718
|
+
const relativeToBase = path.relative(realBaseDir, resolvedSource);
|
|
37719
|
+
if (relativeToBase === ".." || relativeToBase.startsWith(".." + path.sep) || path.isAbsolute(relativeToBase)) return;
|
|
37720
|
+
if (stat.isSymbolicLink()) {
|
|
37721
|
+
source = resolvedSource;
|
|
37722
|
+
stat = await fsp.lstat(source, BIGINT_STAT);
|
|
37723
|
+
if (stat.isSymbolicLink()) return;
|
|
37724
|
+
}
|
|
37725
|
+
}
|
|
37726
|
+
if (stream.destroyed) return;
|
|
37727
|
+
if (filter && !filter(job.source, stat)) return;
|
|
37728
|
+
if (stream.destroyed) return;
|
|
37729
|
+
let header = {
|
|
37730
|
+
name: target,
|
|
37731
|
+
size: 0,
|
|
37732
|
+
mode: (job.mode ?? Number(stat.mode)) & 4095,
|
|
37733
|
+
mtime: job.mtime === void 0 ? stat.mtime : new Date(job.mtime.getTime()),
|
|
37734
|
+
uid: job.uid ?? Number(stat.uid),
|
|
37735
|
+
gid: job.gid ?? Number(stat.gid),
|
|
37736
|
+
uname: job.uname,
|
|
37737
|
+
gname: job.gname,
|
|
37738
|
+
type: FILE
|
|
37739
|
+
};
|
|
37740
|
+
let body;
|
|
37741
|
+
let hardlinkId;
|
|
37742
|
+
if (stat.isDirectory()) {
|
|
37743
|
+
header.type = DIRECTORY;
|
|
37744
|
+
header.name = target.endsWith("/") ? target : `${target}/`;
|
|
37745
|
+
try {
|
|
37746
|
+
const entries = await fsp.readdir(source, WITH_FILE_TYPES);
|
|
37747
|
+
if (stream.destroyed) return;
|
|
37748
|
+
const after = await fsp.lstat(source, BIGINT_STAT);
|
|
37749
|
+
if (stream.destroyed || !after.isDirectory() || stat.dev !== after.dev || stat.ino !== after.ino) return;
|
|
37750
|
+
for (const d of entries) jobs.push({
|
|
37751
|
+
type: d.isDirectory() ? DIRECTORY : FILE,
|
|
37752
|
+
source: path.join(source, d.name),
|
|
37753
|
+
target: `${header.name}${d.name}`,
|
|
37754
|
+
mtime: job.mtime,
|
|
37755
|
+
uid: job.uid,
|
|
37756
|
+
gid: job.gid,
|
|
37757
|
+
uname: job.uname,
|
|
37758
|
+
gname: job.gname,
|
|
37759
|
+
mode: job.mode
|
|
37760
|
+
});
|
|
37761
|
+
} catch (error) {
|
|
37762
|
+
const code = error.code;
|
|
37763
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
37764
|
+
}
|
|
37765
|
+
} else if (stat.isSymbolicLink()) {
|
|
37766
|
+
header.type = SYMLINK;
|
|
37767
|
+
header.linkname = await fsp.readlink(job.source);
|
|
37768
|
+
} else if (stat.isFile()) {
|
|
37769
|
+
header.size = Number(stat.size);
|
|
37770
|
+
let handleToClose;
|
|
37771
|
+
if (stat.nlink > 1n && (process.platform !== "win32" || stat.dev !== 0n && stat.ino !== -1n)) hardlinkId = `${stat.dev}:${stat.ino}`;
|
|
37772
|
+
try {
|
|
37773
|
+
let after;
|
|
37774
|
+
try {
|
|
37775
|
+
if (header.size === 0) after = await fsp.lstat(source, BIGINT_STAT);
|
|
37776
|
+
else {
|
|
37777
|
+
handleToClose = await fsp.open(source, fs.constants.O_NOFOLLOW ?? 0);
|
|
37778
|
+
fileHandles.set(handleToClose, null);
|
|
37779
|
+
}
|
|
37780
|
+
} catch (error) {
|
|
37781
|
+
const code = error.code;
|
|
37782
|
+
if (code === "ELOOP" || code === "ENOENT") return;
|
|
37783
|
+
throw error;
|
|
37784
|
+
}
|
|
37785
|
+
if (stream.destroyed) return;
|
|
37786
|
+
if (after) {
|
|
37787
|
+
if (!after.isFile() || stat.dev !== after.dev || stat.ino !== after.ino) return;
|
|
37788
|
+
} else {
|
|
37789
|
+
const { dev, ino } = await handleToClose.stat(BIGINT_STAT);
|
|
37790
|
+
if (stream.destroyed) return;
|
|
37791
|
+
if (stat.dev !== dev || stat.ino !== ino) return;
|
|
37792
|
+
}
|
|
37793
|
+
if (header.size > 0) {
|
|
37794
|
+
const handle = handleToClose;
|
|
37795
|
+
if (header.size < 32768) {
|
|
37796
|
+
const buffer = Buffer.allocUnsafe(header.size);
|
|
37797
|
+
let offset = 0;
|
|
37798
|
+
while (offset < buffer.length && !stream.destroyed) {
|
|
37799
|
+
const { bytesRead } = await handle.read(buffer, offset, buffer.length - offset, offset);
|
|
37800
|
+
if (bytesRead === 0) break;
|
|
37801
|
+
offset += bytesRead;
|
|
37802
|
+
}
|
|
37803
|
+
body = offset === buffer.length ? buffer : buffer.subarray(0, offset);
|
|
37804
|
+
} else {
|
|
37805
|
+
body = {
|
|
37806
|
+
handle,
|
|
37807
|
+
size: header.size
|
|
37808
|
+
};
|
|
37809
|
+
handleToClose = void 0;
|
|
37810
|
+
}
|
|
37811
|
+
}
|
|
37812
|
+
} finally {
|
|
37813
|
+
if (handleToClose) await closeHandle(handleToClose);
|
|
37814
|
+
}
|
|
37815
|
+
} else return;
|
|
37816
|
+
if (stream.destroyed) return;
|
|
37817
|
+
if (hardlinkId) jobResult = {
|
|
37818
|
+
header,
|
|
37819
|
+
body,
|
|
37820
|
+
hardlinkId
|
|
37821
|
+
};
|
|
37822
|
+
else {
|
|
37823
|
+
if (map) header = map(header);
|
|
37824
|
+
jobResult = {
|
|
37825
|
+
header,
|
|
37826
|
+
body
|
|
37827
|
+
};
|
|
37828
|
+
}
|
|
37829
|
+
} finally {
|
|
37830
|
+
if (stream.destroyed) {
|
|
37831
|
+
if (jobResult?.body instanceof Readable) destroyBody(jobResult.body, cancelError);
|
|
37832
|
+
else if (jobResult?.body && !(jobResult.body instanceof Uint8Array)) await closeHandle(jobResult.body.handle);
|
|
37833
|
+
} else {
|
|
37834
|
+
results.set(index, jobResult);
|
|
37835
|
+
if (index === writeIndex) wakeWriter();
|
|
37836
|
+
}
|
|
37837
|
+
}
|
|
37838
|
+
};
|
|
37839
|
+
controller();
|
|
37840
|
+
await writer();
|
|
37841
|
+
if (!stream.destroyed) {
|
|
37842
|
+
packer.finalize();
|
|
37843
|
+
stream.push(null);
|
|
37844
|
+
}
|
|
37845
|
+
})().catch(onError);
|
|
37846
|
+
return stream;
|
|
37847
|
+
}
|
|
37848
|
+
fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_TRUNC | (fs.constants.O_NOFOLLOW ?? 0) | fs.constants.O_EXCL;
|
|
37849
|
+
Promise.resolve();
|
|
37850
|
+
process.platform;
|
|
37589
37851
|
// src/helper/definitions.ts
|
|
37590
|
-
/** Every problem of every path, per
|
|
37591
|
-
var Rejected = TaggedStruct("Rejected", {
|
|
37592
|
-
skills: ArraySchema(Problem),
|
|
37593
|
-
agents: ArraySchema(Problem)
|
|
37594
|
-
});
|
|
37852
|
+
/** Every problem of every path, per kind; nothing is sent. */
|
|
37853
|
+
var Rejected = TaggedStruct("Rejected", { ...map$8(archiveKinds, () => ArraySchema(Problem)) });
|
|
37595
37854
|
var decoder = new TextDecoder();
|
|
37596
37855
|
var encoder = new TextEncoder();
|
|
37597
|
-
/** A definition file as the helper sends it: its text with the frontmatter repaired when strict YAML rejects it, and the
|
|
37598
|
-
var definitionFile = (
|
|
37856
|
+
/** A definition file as the helper sends it: its text with the frontmatter repaired when strict YAML rejects it, and the package source carrying that text. */
|
|
37857
|
+
var definitionFile = (target, bytes, mode) => {
|
|
37599
37858
|
const original = decoder.decode(bytes);
|
|
37600
37859
|
const text = repairFrontmatter(original);
|
|
37601
37860
|
return {
|
|
37602
37861
|
text,
|
|
37603
|
-
|
|
37604
|
-
|
|
37605
|
-
|
|
37862
|
+
source: {
|
|
37863
|
+
type: "content",
|
|
37864
|
+
target,
|
|
37865
|
+
content: text === original ? bytes : encoder.encode(text),
|
|
37606
37866
|
mode
|
|
37607
37867
|
}
|
|
37608
37868
|
};
|
|
@@ -37613,87 +37873,136 @@ var problemOf = (path, message) => ({
|
|
|
37613
37873
|
message
|
|
37614
37874
|
});
|
|
37615
37875
|
var isNotFound = (cause) => hasProperty(cause, "code") && cause.code === "ENOENT";
|
|
37616
|
-
var
|
|
37876
|
+
var statPath = (path) => tryPromise({
|
|
37617
37877
|
try: () => stat(path),
|
|
37618
37878
|
catch: (cause) => problemOf(path, isNotFound(cause) ? "does not exist" : `cannot be read: ${String(cause)}`)
|
|
37619
|
-
})
|
|
37620
|
-
var
|
|
37621
|
-
|
|
37622
|
-
|
|
37623
|
-
|
|
37624
|
-
withFileTypes: true
|
|
37625
|
-
});
|
|
37626
|
-
return (await Promise.all(dirents.map(async (dirent) => {
|
|
37627
|
-
const file = join(dirent.parentPath, dirent.name);
|
|
37628
|
-
const info = await stat(file);
|
|
37629
|
-
if (!info.isFile()) return;
|
|
37630
|
-
return {
|
|
37631
|
-
path: `${folder}/${relative(path, file).split(sep).join("/")}`,
|
|
37632
|
-
bytes: await readFile(file),
|
|
37633
|
-
mode: info.mode & 511
|
|
37634
|
-
};
|
|
37635
|
-
}))).filter((entry) => entry != null).toSorted((left, right) => left.path.localeCompare(right.path));
|
|
37636
|
-
},
|
|
37637
|
-
catch: (cause) => problemOf(path, `cannot be read: ${String(cause)}`)
|
|
37879
|
+
});
|
|
37880
|
+
var inspect = (path, kind) => statPath(path).pipe(filterOrFail((info) => kind === "folder" ? info.isDirectory() : info.isFile(), () => problemOf(path, `is not a ${kind}`)));
|
|
37881
|
+
var readText = (path, problemPath) => tryPromise({
|
|
37882
|
+
try: () => readFile(path),
|
|
37883
|
+
catch: (cause) => problemOf(problemPath, `cannot be read: ${String(cause)}`)
|
|
37638
37884
|
});
|
|
37639
37885
|
var readSkill = (path) => gen(function* () {
|
|
37640
37886
|
yield* inspect(path, "folder");
|
|
37641
|
-
const
|
|
37642
|
-
const
|
|
37643
|
-
const
|
|
37644
|
-
|
|
37645
|
-
const
|
|
37887
|
+
const root = resolve(path);
|
|
37888
|
+
const folder = basename(root);
|
|
37889
|
+
const skillPath = join(root, skillFile);
|
|
37890
|
+
const missing = problemOf(path, `has no ${skillFile}`);
|
|
37891
|
+
const info = yield* tryPromise({
|
|
37892
|
+
try: () => stat(skillPath),
|
|
37893
|
+
catch: (cause) => isNotFound(cause) ? missing : problemOf(path, `cannot be read: ${String(cause)}`)
|
|
37894
|
+
}).pipe(filterOrFail((found) => found.isFile(), () => missing));
|
|
37895
|
+
const { text, source } = definitionFile(`${folder}/${skillFile}`, yield* readText(skillPath, path), info.mode & 511);
|
|
37896
|
+
const realRoot = yield* tryPromise({
|
|
37897
|
+
try: () => realpath(root),
|
|
37898
|
+
catch: (cause) => problemOf(path, `cannot be read: ${String(cause)}`)
|
|
37899
|
+
});
|
|
37646
37900
|
return {
|
|
37647
|
-
|
|
37901
|
+
skill: {
|
|
37648
37902
|
folder,
|
|
37649
37903
|
path,
|
|
37650
37904
|
text
|
|
37651
37905
|
},
|
|
37652
|
-
|
|
37906
|
+
sources: [{
|
|
37907
|
+
type: "directory",
|
|
37908
|
+
source: realRoot,
|
|
37909
|
+
target: folder
|
|
37910
|
+
}, source],
|
|
37911
|
+
replaced: join(realRoot, skillFile)
|
|
37653
37912
|
};
|
|
37654
37913
|
});
|
|
37655
37914
|
var readAgent = (path) => gen(function* () {
|
|
37656
37915
|
const info = yield* inspect(path, "file");
|
|
37657
|
-
const
|
|
37658
|
-
try: () => readFile(path),
|
|
37659
|
-
catch: (cause) => problemOf(path, `cannot be read: ${String(cause)}`)
|
|
37660
|
-
});
|
|
37661
|
-
const { text, entry } = definitionFile(basename(path), bytes, info.mode & 511);
|
|
37916
|
+
const { text, source } = definitionFile(basename(path), yield* readText(path, path), info.mode & 511);
|
|
37662
37917
|
return {
|
|
37663
|
-
|
|
37918
|
+
agent: {
|
|
37664
37919
|
path,
|
|
37665
37920
|
text
|
|
37666
37921
|
},
|
|
37667
|
-
|
|
37922
|
+
sources: [source]
|
|
37668
37923
|
};
|
|
37669
37924
|
});
|
|
37670
|
-
|
|
37671
|
-
|
|
37672
|
-
|
|
37673
|
-
|
|
37674
|
-
|
|
37675
|
-
|
|
37925
|
+
/** A context path is checked by its stat alone: nothing in a folder is read here. */
|
|
37926
|
+
var readContext = (path) => gen(function* () {
|
|
37927
|
+
const info = yield* statPath(path);
|
|
37928
|
+
if (!info.isDirectory() && !info.isFile()) return yield* fail$3(problemOf(path, "is not a file or a folder"));
|
|
37929
|
+
const source = resolve(path);
|
|
37930
|
+
const target = basename(source);
|
|
37931
|
+
return info.isDirectory() ? {
|
|
37932
|
+
type: "directory",
|
|
37933
|
+
source,
|
|
37934
|
+
target
|
|
37935
|
+
} : {
|
|
37936
|
+
type: "file",
|
|
37937
|
+
source,
|
|
37938
|
+
target
|
|
37939
|
+
};
|
|
37940
|
+
});
|
|
37941
|
+
/** A problem on every context path whose name an earlier path already has. */
|
|
37942
|
+
var duplicateNames = (paths) => {
|
|
37943
|
+
const firstByName = /* @__PURE__ */ new Map();
|
|
37944
|
+
return paths.flatMap((path) => {
|
|
37945
|
+
const name = basename(resolve(path));
|
|
37946
|
+
const first = firstByName.get(name);
|
|
37947
|
+
if (first == null) {
|
|
37948
|
+
firstByName.set(name, path);
|
|
37949
|
+
return [];
|
|
37950
|
+
}
|
|
37951
|
+
return [problemOf(path, `has the same name as ${first}`)];
|
|
37952
|
+
});
|
|
37953
|
+
};
|
|
37954
|
+
/** Checks every path with the kind's reader (a stat for context paths, a read of the definition file for skills and agents), adds the problems the kind finds across the readable ones, and builds the result only when there is none. */
|
|
37955
|
+
var checkPaths = (paths, read, problemsOf, build) => gen(function* () {
|
|
37956
|
+
const [reads, unreadable] = yield* partition(paths, read, { concurrency: "unbounded" });
|
|
37957
|
+
const problems = [...unreadable, ...problemsOf(reads)];
|
|
37958
|
+
return problems.length > 0 ? yield* fail$3(problems) : build(reads);
|
|
37959
|
+
});
|
|
37960
|
+
var checkSkillPaths = (paths) => checkPaths(paths, readSkill, (reads) => checkSkills(reads.map((read) => read.skill)).problems, (reads) => {
|
|
37961
|
+
const replaced = new Set(reads.map((read) => read.replaced));
|
|
37962
|
+
return {
|
|
37963
|
+
sources: reads.flatMap((read) => read.sources),
|
|
37964
|
+
filter: (path) => !replaced.has(path)
|
|
37965
|
+
};
|
|
37966
|
+
});
|
|
37967
|
+
var checkAgentPaths = (paths) => checkPaths(paths, readAgent, (reads) => checkAgents(reads.map((read) => read.agent)).problems, (reads) => ({ sources: reads.flatMap((read) => read.sources) }));
|
|
37968
|
+
var checkContextPaths = (paths) => checkPaths(paths, readContext, () => duplicateNames(paths), (sources) => ({ sources }));
|
|
37969
|
+
var checks = {
|
|
37970
|
+
skills: checkSkillPaths,
|
|
37971
|
+
agents: checkAgentPaths,
|
|
37972
|
+
context: checkContextPaths
|
|
37973
|
+
};
|
|
37974
|
+
var pack = (kind, { sources, ...options }) => tryPromise({
|
|
37975
|
+
try: async () => {
|
|
37976
|
+
const chunks = [];
|
|
37977
|
+
await pipeline(packTar(sources, {
|
|
37978
|
+
dereference: true,
|
|
37979
|
+
baseDir: "/",
|
|
37980
|
+
...options
|
|
37981
|
+
}), createGzip(), async (packed) => {
|
|
37982
|
+
for await (const chunk of packed) chunks.push(chunk);
|
|
37983
|
+
});
|
|
37984
|
+
return Buffer.concat(chunks);
|
|
37985
|
+
},
|
|
37986
|
+
catch: (cause) => [problemOf(`the ${kind} archive`, `cannot be packed: ${String(cause)}`)]
|
|
37987
|
+
}).pipe(filterOrFail((archive) => archive.byteLength <= maxArchiveBytes, (archive) => [problemOf(`the ${kind} archive`, `is ${megabytes(archive.byteLength)}, over the ${megabytes(maxArchiveBytes)} limit`)]));
|
|
37988
|
+
var archiveOf = (kind, paths) => gen(function* () {
|
|
37989
|
+
if (paths.length === 0) return;
|
|
37990
|
+
return yield* checks[kind](paths).pipe(flatMap((checked) => pack(kind, checked)));
|
|
37676
37991
|
}).pipe(result$1);
|
|
37677
|
-
/**
|
|
37678
|
-
var
|
|
37679
|
-
const
|
|
37680
|
-
|
|
37681
|
-
if (isFailure(skills) || isFailure(agents)) return yield* fail$3(Rejected.make({
|
|
37682
|
-
skills: isFailure(skills) ? skills.failure : [],
|
|
37683
|
-
agents: isFailure(agents) ? agents.failure : []
|
|
37684
|
-
}));
|
|
37992
|
+
/** Checks every path the agent passed, kind by kind, and packs each kind that had paths into an archive; fails with every problem of every path, never at the first. */
|
|
37993
|
+
var packSpawnArchives = (paths) => gen(function* () {
|
|
37994
|
+
const results = yield* all$1(map$8(paths, (kindPaths, kind) => archiveOf(kind, kindPaths)), { concurrency: "unbounded" });
|
|
37995
|
+
if (Object.values(results).some(isFailure)) return yield* fail$3(Rejected.make(map$8(results, (result) => isFailure(result) ? result.failure : [])));
|
|
37685
37996
|
const archives = {};
|
|
37686
|
-
|
|
37687
|
-
|
|
37997
|
+
for (const kind of archiveKindNames) {
|
|
37998
|
+
const result = results[kind];
|
|
37999
|
+
if (isSuccess$1(result) && result.success != null) archives[kind] = result.success;
|
|
38000
|
+
}
|
|
37688
38001
|
return archives;
|
|
37689
38002
|
});
|
|
37690
38003
|
var section = (heading, problems) => `${heading}:\n${problems.map((problem) => `- ${problem.path}: ${problem.message}`).join("\n")}`;
|
|
37691
|
-
/** The tool's error text: every problem with its path,
|
|
37692
|
-
var rejectionText = (rejected) => [
|
|
37693
|
-
"No session was created: some skills or agents have problems.",
|
|
37694
|
-
...rejected.skills.length > 0 ? [section("Skills", rejected.skills)] : [],
|
|
37695
|
-
...rejected.agents.length > 0 ? [section("Agents", rejected.agents)] : []
|
|
37696
|
-
].join("\n\n");
|
|
38004
|
+
/** The tool's error text: every problem with its path, one section per kind with problems, in the kinds table's order. */
|
|
38005
|
+
var rejectionText = (rejected) => ["No session was created: some skills, agents or context have problems.", ...archiveKindNames.flatMap((kind) => rejected[kind].length > 0 ? [section(archiveKinds[kind].heading, rejected[kind])] : [])].join("\n\n");
|
|
37697
38006
|
// src/helper/command.ts
|
|
37698
38007
|
var sessionFields = { sessionId: SessionId };
|
|
37699
38008
|
var PromptRequest = Struct({
|
|
@@ -37721,9 +38030,10 @@ var commands = {
|
|
|
37721
38030
|
const request = yield* decodeArguments(SpawnRequest, json);
|
|
37722
38031
|
const defaults = yield* SettingsDefaults;
|
|
37723
38032
|
const client = yield* TentaclesClient;
|
|
37724
|
-
const archives = yield*
|
|
38033
|
+
const archives = yield* packSpawnArchives({
|
|
37725
38034
|
skills: request.skills ?? [],
|
|
37726
|
-
agents: request.agents ?? []
|
|
38035
|
+
agents: request.agents ?? [],
|
|
38036
|
+
context: request.context ?? []
|
|
37727
38037
|
});
|
|
37728
38038
|
return yield* client.sessions.create({ payload: {
|
|
37729
38039
|
name: request.name,
|