@foldspace_npm/harness 0.1.25 → 0.1.26
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/CLAUDE.md +6 -3
- package/README.md +6 -0
- package/package.json +1 -1
- package/src/attach-preflight.mjs +2 -1
- package/src/cli-registry.mjs +9 -1
- package/src/init.mjs +9 -0
- package/src/region.mjs +54 -0
- package/templates/agent-starter/foldspace.dev.json +2 -1
package/CLAUDE.md
CHANGED
|
@@ -21,12 +21,15 @@ something they can **see on their own page** — not at "it builds".
|
|
|
21
21
|
project `CLAUDE.md` does not contain
|
|
22
22
|
`@node_modules/@foldspace_npm/harness/CLAUDE.md`, ask before
|
|
23
23
|
`foldspace upgrade --refresh-instructions`.
|
|
24
|
-
2. **Is Foldspace connected? Check — never assume.**
|
|
24
|
+
2. **Is Foldspace connected? Check — never assume.** `region` in
|
|
25
|
+
`foldspace.dev.json` (absent means `us`) picks the server: `foldspace`, or
|
|
26
|
+
`foldspace-eu` for `eu`. Use that server's tools all session. Call its
|
|
27
|
+
`list_agents`:
|
|
25
28
|
|
|
26
29
|
| Result | Do |
|
|
27
30
|
|---|---|
|
|
28
|
-
| The tool does not exist | The Foldspace MCP is not installed in this editor. Give the one install step for *this* editor — Claude Code: `claude plugin install foldspace-agent-builder@foldspace-plugins`; anything else: the MCP block on <https://foldspace.ai/agents.md
|
|
29
|
-
| It exists but fails on auth | Installed, not signed in. Only the human can approve sign-in. Tell them exactly where — Claude Code: `/mcp` → **foldspace** → finish in the browser — then check again |
|
|
31
|
+
| The tool does not exist | The Foldspace MCP is not installed in this editor. Give the one install step for *this* region and editor — **EU**, any editor: the MCP block on <https://foldspace.ai/eu/agents.md>, never the plugin. **US** — Claude Code: `claude plugin install foldspace-agent-builder@foldspace-plugins`; anything else: the MCP block on <https://foldspace.ai/agents.md>. Then check again |
|
|
32
|
+
| It exists but fails on auth | Installed, not signed in. Only the human can approve sign-in. Tell them exactly where — Claude Code: `/mcp` → **foldspace-eu** (**EU**) or **foldspace** (**US**) → finish in the browser — then check again |
|
|
30
33
|
| It returns agents | One agent: state its name, Agent Key and product in a line and carry on; the human objects if it is wrong. Several: ask which, as a choice |
|
|
31
34
|
|
|
32
35
|
3. **Orient** — `get_agent_settings`, `list_actions`, `list_task_agents`,
|
package/README.md
CHANGED
|
@@ -118,6 +118,12 @@ name (`foldspace-agent` unless you pass a directory). The product ID must be
|
|
|
118
118
|
the bare ID, not the `EU-…-1-1` SDK loader key. The app domain may be a hostname
|
|
119
119
|
or an HTTP(S) URL without a port or path.
|
|
120
120
|
|
|
121
|
+
`--region us|eu` (default `us`) is the Foldspace region the account lives in.
|
|
122
|
+
It is written to `foldspace.dev.json` as `region`, and picks the SDK the
|
|
123
|
+
project loads (`sdkUrl`): each region has its own build, pointing at that
|
|
124
|
+
region's servers. A project with no `region` is US. The values per region live
|
|
125
|
+
in `src/region.mjs`.
|
|
126
|
+
|
|
121
127
|
For safety, `init` requires a target path that does not exist. It does not
|
|
122
128
|
initialize Git or overwrite files. On a TTY it can offer
|
|
123
129
|
`npm install --ignore-scripts` after scaffolding. After creation:
|
package/package.json
CHANGED
package/src/attach-preflight.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
parseActionRequest,
|
|
5
5
|
} from "./attach-helpers.mjs";
|
|
6
6
|
import { ERROR_CODES } from "./protocol.mjs";
|
|
7
|
+
import { SDK_SCRIPT_HOSTS } from "./region.mjs";
|
|
7
8
|
|
|
8
9
|
export function productIdFromAgentKey(key) {
|
|
9
10
|
if (typeof key !== "string") return null;
|
|
@@ -135,7 +136,7 @@ function sdkScriptIdentity(url) {
|
|
|
135
136
|
const parsed = new URL(url);
|
|
136
137
|
if (
|
|
137
138
|
parsed.protocol !== "https:" ||
|
|
138
|
-
parsed.hostname
|
|
139
|
+
!SDK_SCRIPT_HOSTS.includes(parsed.hostname) ||
|
|
139
140
|
!/^\/web\/sdk\/(?:foldspace|eucera)\.js$/i.test(parsed.pathname)
|
|
140
141
|
) {
|
|
141
142
|
return null;
|
package/src/cli-registry.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { diagnosticCatalogue } from "./diagnostics.mjs";
|
|
2
2
|
import { HARNESS_PROTOCOL_VERSION } from "./protocol.mjs";
|
|
3
|
+
import { DEFAULT_REGION, REGION_NAMES } from "./region.mjs";
|
|
3
4
|
|
|
4
5
|
export const CLI_SCHEMA_VERSION = 1;
|
|
5
6
|
|
|
@@ -21,7 +22,8 @@ export const CLI_COMMANDS = Object.freeze([
|
|
|
21
22
|
group: "start",
|
|
22
23
|
summary: "Create a configured Foldspace actions project",
|
|
23
24
|
usage:
|
|
24
|
-
"foldspace init [<directory>] [--product-id <id>] [--agent-key <key>] [--app-domain <host>] [--name <display-name>]"
|
|
25
|
+
"foldspace init [<directory>] [--product-id <id>] [--agent-key <key>] [--app-domain <host>] [--name <display-name>] " +
|
|
26
|
+
`[--region <${REGION_NAMES.join("|")}>]`,
|
|
25
27
|
risk: "local-write",
|
|
26
28
|
environment: "node",
|
|
27
29
|
environmentVariables: [],
|
|
@@ -48,6 +50,12 @@ export const CLI_COMMANDS = Object.freeze([
|
|
|
48
50
|
value("--name", "display-name", "Display name", {
|
|
49
51
|
default: "directory name",
|
|
50
52
|
}),
|
|
53
|
+
value(
|
|
54
|
+
"--region",
|
|
55
|
+
REGION_NAMES.join("|"),
|
|
56
|
+
"Foldspace region of the account; sets the SDK the project loads",
|
|
57
|
+
{ default: DEFAULT_REGION, choices: REGION_NAMES },
|
|
58
|
+
),
|
|
51
59
|
],
|
|
52
60
|
prerequisites: [
|
|
53
61
|
"Node 20 or newer",
|
package/src/init.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import readline from "node:readline/promises";
|
|
|
5
5
|
import { stdin as input, stdout as output } from "node:process";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { commandByName } from "./cli-registry.mjs";
|
|
8
|
+
import { DEFAULT_REGION, REGIONS, parseRegion } from "./region.mjs";
|
|
8
9
|
|
|
9
10
|
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
11
|
const defaultTemplateRoot = path.join(packageRoot, "templates", "agent-starter");
|
|
@@ -15,6 +16,7 @@ const allowedFlags = new Set([
|
|
|
15
16
|
"agent-api-name",
|
|
16
17
|
"app-domain",
|
|
17
18
|
"domain",
|
|
19
|
+
"region",
|
|
18
20
|
]);
|
|
19
21
|
const tokenPattern = /\{\{([A-Z0-9_]+)\}\}/g;
|
|
20
22
|
const defaultDirectory = "foldspace-agent";
|
|
@@ -99,6 +101,9 @@ export function parseInitArgs(argv) {
|
|
|
99
101
|
productId: flags["product-id"] || null,
|
|
100
102
|
agentApiName: flags["agent-key"] || flags["agent-api-name"] || null,
|
|
101
103
|
domain: flags["app-domain"] || flags.domain || null,
|
|
104
|
+
// Checked here, before any prompt: a typo must not cost a whole
|
|
105
|
+
// interactive run and then fail at the end.
|
|
106
|
+
region: flags.region ? parseRegion(flags.region) : null,
|
|
102
107
|
};
|
|
103
108
|
}
|
|
104
109
|
|
|
@@ -275,6 +280,7 @@ function finalizeInitConfig(parsed) {
|
|
|
275
280
|
productId: parsed.productId,
|
|
276
281
|
agentApiName: parsed.agentApiName,
|
|
277
282
|
domain: parsed.domain,
|
|
283
|
+
region: parsed.region || DEFAULT_REGION,
|
|
278
284
|
};
|
|
279
285
|
}
|
|
280
286
|
|
|
@@ -300,6 +306,7 @@ function createTemplateValues(config, harnessVersion) {
|
|
|
300
306
|
|
|
301
307
|
const productId = validateProductId(config.productId);
|
|
302
308
|
const agentApiName = validateIdentifier(config.agentApiName, "Agent Key");
|
|
309
|
+
const region = parseRegion(config.region || DEFAULT_REGION);
|
|
303
310
|
const target = normalizeTarget(config.domain);
|
|
304
311
|
const hosts =
|
|
305
312
|
target.domain === "localhost" || /^\d+(?:\.\d+){3}$/.test(target.domain)
|
|
@@ -319,6 +326,8 @@ function createTemplateValues(config, harnessVersion) {
|
|
|
319
326
|
START_URL: target.startUrl,
|
|
320
327
|
START_URL_JSON: JSON.stringify(target.startUrl),
|
|
321
328
|
HOSTS_JSON: JSON.stringify(hosts),
|
|
329
|
+
REGION_JSON: JSON.stringify(region),
|
|
330
|
+
SDK_URL_JSON: JSON.stringify(REGIONS[region].sdkUrl),
|
|
322
331
|
HARNESS_VERSION: harnessVersion,
|
|
323
332
|
};
|
|
324
333
|
}
|
package/src/region.mjs
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// The Foldspace regions a project can live in. Everything region-specific in
|
|
2
|
+
// the harness comes from here: init writes `region` and its `sdkUrl` into
|
|
3
|
+
// foldspace.dev.json, attach recognizes the SDK on every region's script host,
|
|
4
|
+
// and the startup step in CLAUDE.md names each region's server and playbook.
|
|
5
|
+
//
|
|
6
|
+
// `sdkUrl`: each region has its own SDK build. Same code, but its config
|
|
7
|
+
// points at that region's agent, recording and tracking hosts - the US SDK on
|
|
8
|
+
// an EU page talks to a US backend that does not have the EU agents.
|
|
9
|
+
//
|
|
10
|
+
// `server`: the Product MCP's name in the editor, as the region's playbook
|
|
11
|
+
// installs it. Distinct per region, so both can sit side by side.
|
|
12
|
+
export const REGIONS = Object.freeze({
|
|
13
|
+
us: Object.freeze({
|
|
14
|
+
label: "US",
|
|
15
|
+
sdkUrl: "https://script.eucerahive.io/web/sdk/foldspace.js",
|
|
16
|
+
server: "foldspace",
|
|
17
|
+
playbook: "https://foldspace.ai/agents.md",
|
|
18
|
+
}),
|
|
19
|
+
eu: Object.freeze({
|
|
20
|
+
label: "EU",
|
|
21
|
+
sdkUrl: "https://script.eu.eucerahive.io/web/sdk/foldspace.js",
|
|
22
|
+
server: "foldspace-eu",
|
|
23
|
+
playbook: "https://foldspace.ai/eu/agents.md",
|
|
24
|
+
}),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Projects created before regions existed have no `region`: they are US.
|
|
28
|
+
export const DEFAULT_REGION = "us";
|
|
29
|
+
|
|
30
|
+
export const REGION_NAMES = Object.freeze(Object.keys(REGIONS));
|
|
31
|
+
|
|
32
|
+
export function isRegion(value) {
|
|
33
|
+
return typeof value === "string" && Object.hasOwn(REGIONS, value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function parseRegion(value) {
|
|
37
|
+
const region = typeof value === "string" ? value.trim().toLowerCase() : value;
|
|
38
|
+
if (!isRegion(region)) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Unknown region: ${value}. Use one of: ${REGION_NAMES.join(", ")}.`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return region;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The region a foldspace.dev.json belongs to.
|
|
47
|
+
export function regionOf(config) {
|
|
48
|
+
return REGIONS[parseRegion(config?.region ?? DEFAULT_REGION)];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Hostnames the Foldspace SDK is served from, in any region.
|
|
52
|
+
export const SDK_SCRIPT_HOSTS = Object.freeze(
|
|
53
|
+
REGION_NAMES.map((name) => new URL(REGIONS[name].sdkUrl).hostname),
|
|
54
|
+
);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "Generated by foldspace init. Use the bare product ID, not the EU-… SDK key.",
|
|
3
3
|
"defaultTarget": "app",
|
|
4
|
-
"
|
|
4
|
+
"region": {{REGION_JSON}},
|
|
5
|
+
"sdkUrl": {{SDK_URL_JSON}},
|
|
5
6
|
"targets": {
|
|
6
7
|
"app": {
|
|
7
8
|
"name": "App",
|