@foldspace_npm/harness 0.1.2 → 0.1.4
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 +67 -16
- package/bin/attach.mjs +779 -107
- package/bin/cli.mjs +129 -44
- package/bin/deploy.mjs +37 -33
- package/bin/inject.mjs +64 -295
- package/package.json +5 -2
- package/src/action-observer.mjs +271 -0
- package/src/attach-helpers.mjs +162 -0
- package/src/attach-preflight.mjs +332 -0
- package/src/bootstrap-script.mjs +120 -0
- package/src/cdp-request-manager.mjs +61 -0
- package/src/cli-help.mjs +147 -0
- package/src/cli-registry.mjs +310 -0
- package/src/diagnostics.mjs +482 -0
- package/src/init.mjs +20 -9
- package/src/project-config.mjs +37 -0
- package/src/protocol.mjs +181 -0
- package/src/session-summary.mjs +133 -0
- package/templates/agent-starter/CLAUDE.md +31 -8
- package/templates/agent-starter/README.md +26 -3
- package/templates/agent-starter/foldspace.dev.json +1 -3
- package/bin/buildExtension.mjs +0 -90
- package/bin/packageExtension.mjs +0 -29
package/bin/cli.mjs
CHANGED
|
@@ -1,43 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
|
+
import fs from "node:fs";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import {
|
|
7
|
+
import { runInit } from "../src/init.mjs";
|
|
8
|
+
import {
|
|
9
|
+
CLI_COMMANDS,
|
|
10
|
+
createCliRegistry,
|
|
11
|
+
commandByName,
|
|
12
|
+
} from "../src/cli-registry.mjs";
|
|
13
|
+
import {
|
|
14
|
+
helpDocument,
|
|
15
|
+
renderCommandHelp,
|
|
16
|
+
renderGeneralHelp,
|
|
17
|
+
} from "../src/cli-help.mjs";
|
|
7
18
|
|
|
8
19
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"package-extension": "Package the browser extension",
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const scriptByCommand = {
|
|
20
|
-
build: "build-cli.mjs",
|
|
21
|
-
inject: "inject.mjs",
|
|
22
|
-
attach: "attach.mjs",
|
|
23
|
-
deploy: "deploy.mjs",
|
|
24
|
-
"package-extension": "packageExtension.mjs",
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const usage = `Usage:
|
|
28
|
-
foldspace <command> [args]
|
|
29
|
-
${initUsage}
|
|
30
|
-
foldspace build [--watch]
|
|
31
|
-
foldspace inject [options]
|
|
32
|
-
foldspace attach [options]
|
|
33
|
-
foldspace deploy [options]
|
|
34
|
-
foldspace package-extension
|
|
35
|
-
|
|
36
|
-
Commands:
|
|
37
|
-
${Object.entries(commands)
|
|
38
|
-
.map(([name, description]) => ` ${name.padEnd(18)} ${description}`)
|
|
39
|
-
.join("\n")}
|
|
40
|
-
`;
|
|
20
|
+
const packageManifest = JSON.parse(
|
|
21
|
+
fs.readFileSync(path.resolve(here, "..", "package.json"), "utf8"),
|
|
22
|
+
);
|
|
23
|
+
const registry = createCliRegistry({
|
|
24
|
+
packageName: packageManifest.name,
|
|
25
|
+
packageVersion: packageManifest.version,
|
|
26
|
+
});
|
|
41
27
|
|
|
42
28
|
function fail(message) {
|
|
43
29
|
console.error(`foldspace: ${message}`);
|
|
@@ -58,26 +44,125 @@ function runScript(scriptName, args) {
|
|
|
58
44
|
});
|
|
59
45
|
}
|
|
60
46
|
|
|
47
|
+
function printHelp(topic, json) {
|
|
48
|
+
const document = helpDocument(registry, topic);
|
|
49
|
+
if (!document) {
|
|
50
|
+
fail(
|
|
51
|
+
`unknown help topic '${topic}'. Run 'foldspace help' to list commands.`,
|
|
52
|
+
);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (json) {
|
|
56
|
+
console.log(JSON.stringify(document, null, 2));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
console.log(
|
|
60
|
+
topic ? renderCommandHelp(registry, topic) : renderGeneralHelp(registry),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function normalizeCommandArgs(command, args) {
|
|
65
|
+
const options = new Map();
|
|
66
|
+
for (const option of command.options) {
|
|
67
|
+
options.set(option.name, { option, canonicalName: option.name });
|
|
68
|
+
for (const alias of option.deprecatedAliases || []) {
|
|
69
|
+
options.set(alias, { option, canonicalName: option.name });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const seen = new Set();
|
|
73
|
+
const normalized = [];
|
|
74
|
+
let positionals = 0;
|
|
75
|
+
for (let index = 0; index < args.length; index++) {
|
|
76
|
+
const token = args[index];
|
|
77
|
+
if (!token.startsWith("--")) {
|
|
78
|
+
if (positionals >= command.positionals.length) {
|
|
79
|
+
throw new Error(`unexpected argument '${token}'`);
|
|
80
|
+
}
|
|
81
|
+
positionals += 1;
|
|
82
|
+
normalized.push(token);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const separator = token.indexOf("=");
|
|
87
|
+
const name = separator > -1 ? token.slice(0, separator) : token;
|
|
88
|
+
const match = options.get(name);
|
|
89
|
+
if (!match) throw new Error(`unknown option '${name}'`);
|
|
90
|
+
const { option, canonicalName } = match;
|
|
91
|
+
if (seen.has(canonicalName)) {
|
|
92
|
+
throw new Error(`option '${canonicalName}' was provided twice`);
|
|
93
|
+
}
|
|
94
|
+
seen.add(canonicalName);
|
|
95
|
+
|
|
96
|
+
if (option.kind === "flag") {
|
|
97
|
+
if (separator > -1) throw new Error(`option '${name}' takes no value`);
|
|
98
|
+
normalized.push(canonicalName);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const optionValue =
|
|
103
|
+
separator > -1 ? token.slice(separator + 1) : args[++index];
|
|
104
|
+
if (!optionValue || optionValue.startsWith("--")) {
|
|
105
|
+
throw new Error(`option '${name}' requires a value`);
|
|
106
|
+
}
|
|
107
|
+
normalized.push(canonicalName, optionValue);
|
|
108
|
+
}
|
|
109
|
+
return normalized;
|
|
110
|
+
}
|
|
111
|
+
|
|
61
112
|
const [command, ...args] = process.argv.slice(2);
|
|
62
113
|
|
|
63
114
|
if (!command || command === "--help" || command === "-h") {
|
|
64
|
-
|
|
115
|
+
printHelp(null, args.includes("--json"));
|
|
116
|
+
} else if (command === "--version" || command === "-v") {
|
|
117
|
+
console.log(
|
|
118
|
+
`${registry.package.name} ${registry.package.version} ` +
|
|
119
|
+
`(protocol ${registry.protocolVersion}, CLI schema ${registry.schemaVersion})`,
|
|
120
|
+
);
|
|
121
|
+
} else if (command === "help") {
|
|
122
|
+
const unknown = args.filter(
|
|
123
|
+
(arg) => arg.startsWith("-") && arg !== "--json",
|
|
124
|
+
);
|
|
125
|
+
const topics = args.filter((arg) => !arg.startsWith("-"));
|
|
126
|
+
if (unknown.length || topics.length > 1) {
|
|
127
|
+
fail(
|
|
128
|
+
`usage: foldspace help [<command>] [--json]`,
|
|
129
|
+
);
|
|
130
|
+
} else {
|
|
131
|
+
printHelp(topics[0] || null, args.includes("--json"));
|
|
132
|
+
}
|
|
65
133
|
} else if (command === "init") {
|
|
66
134
|
if (args.includes("--help") || args.includes("-h")) {
|
|
67
|
-
|
|
135
|
+
printHelp("init", args.includes("--json"));
|
|
68
136
|
} else {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
.
|
|
72
|
-
|
|
73
|
-
|
|
137
|
+
try {
|
|
138
|
+
const normalized = normalizeCommandArgs(commandByName("init"), args);
|
|
139
|
+
Promise.resolve()
|
|
140
|
+
.then(() => runInit(normalized))
|
|
141
|
+
.catch((error) => {
|
|
142
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
143
|
+
});
|
|
144
|
+
} catch (error) {
|
|
145
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
146
|
+
}
|
|
74
147
|
}
|
|
75
|
-
} else if (
|
|
148
|
+
} else if (commandByName(command)) {
|
|
76
149
|
if (args.includes("--help") || args.includes("-h")) {
|
|
77
|
-
|
|
150
|
+
printHelp(command, args.includes("--json"));
|
|
78
151
|
} else {
|
|
79
|
-
|
|
152
|
+
try {
|
|
153
|
+
const definition = commandByName(command);
|
|
154
|
+
runScript(
|
|
155
|
+
definition.entry,
|
|
156
|
+
normalizeCommandArgs(definition, args),
|
|
157
|
+
);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
160
|
+
}
|
|
80
161
|
}
|
|
81
162
|
} else {
|
|
82
|
-
|
|
163
|
+
const names = CLI_COMMANDS.map((entry) => entry.name).join(", ");
|
|
164
|
+
fail(
|
|
165
|
+
`unknown command '${command}'. Available commands: ${names}. ` +
|
|
166
|
+
`Run 'foldspace help'.`,
|
|
167
|
+
);
|
|
83
168
|
}
|
package/bin/deploy.mjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { execFileSync, execSync } from "child_process";
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
readProjectConfig,
|
|
6
|
+
resolveConfiguredTarget,
|
|
7
|
+
} from "../src/project-config.mjs";
|
|
5
8
|
|
|
6
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
9
|
const projectDir = process.env.FOLDSPACE_PROJECT_DIR || process.cwd();
|
|
8
10
|
|
|
9
11
|
const GCS_BUCKET = "prod-us1-eucera-public-scripts";
|
|
@@ -12,35 +14,15 @@ function parseArgs(argv) {
|
|
|
12
14
|
const args = {};
|
|
13
15
|
for (let i = 0; i < argv.length; i++) {
|
|
14
16
|
if (argv[i] === "--env" && argv[i + 1]) args.env = argv[++i];
|
|
17
|
+
else if (argv[i] === "--target" && argv[i + 1]) {
|
|
18
|
+
args.target = argv[++i];
|
|
19
|
+
} else {
|
|
20
|
+
throw new Error(`unknown or incomplete option '${argv[i]}'`);
|
|
21
|
+
}
|
|
15
22
|
}
|
|
16
23
|
return args;
|
|
17
24
|
}
|
|
18
25
|
|
|
19
|
-
function readExtensionConfig() {
|
|
20
|
-
const indexPath = path.join(projectDir, "extension", "index.js");
|
|
21
|
-
if (!fs.existsSync(indexPath)) {
|
|
22
|
-
console.error("deploy: extension/index.js not found");
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
const src = fs.readFileSync(indexPath, "utf8");
|
|
26
|
-
|
|
27
|
-
const productMatch = src.match(/const\s+PRODUCT_ID\s*=\s*["'](.+?)["']/);
|
|
28
|
-
const agentMatch = src.match(/const\s+AGENT_API_NAME\s*=\s*["'](.+?)["']/);
|
|
29
|
-
|
|
30
|
-
if (!productMatch || !productMatch[1]) {
|
|
31
|
-
console.error("deploy: could not read PRODUCT_ID from extension/index.js");
|
|
32
|
-
process.exit(1);
|
|
33
|
-
}
|
|
34
|
-
if (!agentMatch || !agentMatch[1]) {
|
|
35
|
-
console.error(
|
|
36
|
-
"deploy: could not read AGENT_API_NAME from extension/index.js",
|
|
37
|
-
);
|
|
38
|
-
process.exit(1);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return { productId: productMatch[1], agentApiName: agentMatch[1] };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
26
|
function validate(args) {
|
|
45
27
|
const errors = [];
|
|
46
28
|
if (args.env !== "dev" && args.env !== "prod")
|
|
@@ -48,7 +30,9 @@ function validate(args) {
|
|
|
48
30
|
if (errors.length) {
|
|
49
31
|
console.error("deploy: validation failed:");
|
|
50
32
|
errors.forEach((e) => console.error(` - ${e}`));
|
|
51
|
-
console.error(
|
|
33
|
+
console.error(
|
|
34
|
+
"\nUsage: foldspace deploy [--target <name>] [--env <dev|prod>]",
|
|
35
|
+
);
|
|
52
36
|
process.exit(1);
|
|
53
37
|
}
|
|
54
38
|
}
|
|
@@ -181,12 +165,32 @@ function updateDeployments(env, version) {
|
|
|
181
165
|
}
|
|
182
166
|
|
|
183
167
|
function deploy() {
|
|
184
|
-
|
|
168
|
+
let args;
|
|
169
|
+
try {
|
|
170
|
+
args = parseArgs(process.argv.slice(2));
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error(
|
|
173
|
+
`deploy: ${error instanceof Error ? error.message : String(error)}`,
|
|
174
|
+
);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
185
177
|
if (!args.env) args.env = "dev";
|
|
186
178
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
179
|
+
let resolved;
|
|
180
|
+
try {
|
|
181
|
+
resolved = resolveConfiguredTarget(
|
|
182
|
+
readProjectConfig(projectDir),
|
|
183
|
+
args.target,
|
|
184
|
+
);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.error(
|
|
187
|
+
`deploy: ${error instanceof Error ? error.message : String(error)}`,
|
|
188
|
+
);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
args.target = resolved.targetName;
|
|
192
|
+
args.productId = resolved.target.productId;
|
|
193
|
+
args.agentApiName = resolved.target.agentApiName;
|
|
190
194
|
|
|
191
195
|
validate(args);
|
|
192
196
|
|
|
@@ -200,7 +204,7 @@ function deploy() {
|
|
|
200
204
|
|
|
201
205
|
const basePath = gcsBasePath(args.env, args.productId, args.agentApiName);
|
|
202
206
|
|
|
203
|
-
console.log(`deploy: ${version} -> ${args.env}`);
|
|
207
|
+
console.log(`deploy: ${version} -> ${args.env} (${args.target})`);
|
|
204
208
|
console.log(`deploy: bucket ${GCS_BUCKET}`);
|
|
205
209
|
console.log(`deploy: path ${basePath}`);
|
|
206
210
|
|