@lingjingai/scriptctl 0.49.7 → 0.49.8
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/changes/0.49.8.md +13 -0
- package/dist/cli.js +37 -28
- package/dist/cli.js.map +1 -1
- package/dist/common.js +2 -4
- package/dist/common.js.map +1 -1
- package/dist/help-text.js +51 -28
- package/dist/help-text.js.map +1 -1
- package/dist/infra/script-output-api.d.ts +9 -6
- package/dist/infra/script-output-api.js +16 -3
- package/dist/infra/script-output-api.js.map +1 -1
- package/dist/infra/script-output-store.d.ts +16 -0
- package/dist/infra/script-output-store.js.map +1 -1
- package/dist/infra/script-working-copy.d.ts +22 -0
- package/dist/infra/script-working-copy.js +155 -0
- package/dist/infra/script-working-copy.js.map +1 -0
- package/dist/usecases/ingest/publish-command.d.ts +1 -4
- package/dist/usecases/ingest/publish-command.js +3 -76
- package/dist/usecases/ingest/publish-command.js.map +1 -1
- package/dist/usecases/script/create.js +12 -48
- package/dist/usecases/script/create.js.map +1 -1
- package/dist/usecases/script/session.d.ts +2 -1
- package/dist/usecases/script/session.js +18 -49
- package/dist/usecases/script/session.js.map +1 -1
- package/dist/usecases/script/working-copy.d.ts +5 -0
- package/dist/usecases/script/working-copy.js +256 -0
- package/dist/usecases/script/working-copy.js.map +1 -0
- package/package.json +3 -1
- package/skills/scriptctl/SKILL.md +15 -22
- package/skills/scriptctl/references/atomic-write-workflow.md +10 -8
- package/skills/scriptctl/references/ingest-workflow.md +8 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"script-output-api.js","sourceRoot":"","sources":["../../src/infra/script-output-api.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,+EAA+E;AAC/E,8EAA8E;AAC9E,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAC5C,MAAM,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC;AAElC,OAAO,EACL,oBAAoB,
|
|
1
|
+
{"version":3,"file":"script-output-api.js","sourceRoot":"","sources":["../../src/infra/script-output-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,6 +24,22 @@ export interface ReplaceScriptOptions {
|
|
|
24
24
|
script: Record<string, unknown>;
|
|
25
25
|
source?: string;
|
|
26
26
|
}
|
|
27
|
+
export interface ScriptSnapshot {
|
|
28
|
+
projectGroupNo: string;
|
|
29
|
+
script: Record<string, unknown>;
|
|
30
|
+
revision: number;
|
|
31
|
+
validationStatus?: string | null;
|
|
32
|
+
validationSummary?: Record<string, unknown> | null;
|
|
33
|
+
updatedAt?: string | null;
|
|
34
|
+
}
|
|
35
|
+
export interface PublishScriptOptions {
|
|
36
|
+
baseRevision: number;
|
|
37
|
+
script: Record<string, unknown>;
|
|
38
|
+
validation: {
|
|
39
|
+
status: "valid";
|
|
40
|
+
summary: Record<string, unknown>;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
27
43
|
/**
|
|
28
44
|
* Incremental edit: publish the RFC6902 diff between the loaded script and the
|
|
29
45
|
* locally-edited script, instead of re-uploading the whole document. `old` is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"script-output-store.js","sourceRoot":"","sources":["../../src/infra/script-output-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAiBhE,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IACpC,MAAM,CAAgB;IACtB,IAAI,CAAyB;IAC7B,OAAO,CAAU;IACjB,aAAa,CAA2B;IAEjD,YAAY,OAAe,EAAE,OAAoC,EAAE;QACjE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC;QAClC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;QAC9B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC;IAClD,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"script-output-store.js","sourceRoot":"","sources":["../../src/infra/script-output-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface WorkingCopyMetadata {
|
|
2
|
+
schemaVersion: 1;
|
|
3
|
+
projectGroupNo: string;
|
|
4
|
+
apiBaseUrl: string;
|
|
5
|
+
baseRevision: number;
|
|
6
|
+
baseContentHash: string;
|
|
7
|
+
}
|
|
8
|
+
export interface WorkingCopyPaths {
|
|
9
|
+
scriptPath: string;
|
|
10
|
+
stateDir: string;
|
|
11
|
+
metadataPath: string;
|
|
12
|
+
lockPath: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function semanticScriptHash(script: unknown): string;
|
|
15
|
+
export declare function localFileFingerprint(filePath: string): string | null;
|
|
16
|
+
export declare function assertLocalFileFingerprint(filePath: string, expected: string | null): void;
|
|
17
|
+
export declare function workingCopyPaths(rawScriptPath: string): WorkingCopyPaths;
|
|
18
|
+
export declare function withScriptFileLock<T>(rawScriptPath: string, operation: () => Promise<T>): Promise<T>;
|
|
19
|
+
export declare function atomicWriteJson(filePath: string, value: unknown): void;
|
|
20
|
+
export declare function readWorkingCopyMetadata(rawScriptPath: string): WorkingCopyMetadata | null;
|
|
21
|
+
export declare function writeWorkingCopyMetadata(rawScriptPath: string, metadata: WorkingCopyMetadata): void;
|
|
22
|
+
export declare function assertScriptUnchanged(scriptPath: string, expectedHash: string): void;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import lockfile from "proper-lockfile";
|
|
5
|
+
import { CliError, EXIT_INPUT, EXIT_RUNTIME } from "../common.js";
|
|
6
|
+
function canonicalize(value) {
|
|
7
|
+
if (Array.isArray(value))
|
|
8
|
+
return value.map(canonicalize);
|
|
9
|
+
if (value !== null && typeof value === "object") {
|
|
10
|
+
const source = value;
|
|
11
|
+
const result = {};
|
|
12
|
+
for (const key of Object.keys(source).sort())
|
|
13
|
+
result[key] = canonicalize(source[key]);
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
export function semanticScriptHash(script) {
|
|
19
|
+
const canonical = JSON.stringify(canonicalize(script));
|
|
20
|
+
return `sha256:${crypto.createHash("sha256").update(canonical).digest("hex")}`;
|
|
21
|
+
}
|
|
22
|
+
export function localFileFingerprint(filePath) {
|
|
23
|
+
if (!fs.existsSync(filePath))
|
|
24
|
+
return null;
|
|
25
|
+
return `sha256:${crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex")}`;
|
|
26
|
+
}
|
|
27
|
+
export function assertLocalFileFingerprint(filePath, expected) {
|
|
28
|
+
const current = localFileFingerprint(filePath);
|
|
29
|
+
if (current === expected)
|
|
30
|
+
return;
|
|
31
|
+
throw new CliError("SCRIPT BLOCKED: local script changed", "The local script changed while the command was running.", {
|
|
32
|
+
exitCode: EXIT_INPUT,
|
|
33
|
+
errorCode: "LOCAL_SCRIPT_CHANGED",
|
|
34
|
+
received: [`loadedFingerprint=${expected ?? "<missing>"}`, `currentFingerprint=${current ?? "<missing>"}`, filePath],
|
|
35
|
+
nextSteps: ["Read the latest local script and retry the command."],
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export function workingCopyPaths(rawScriptPath) {
|
|
39
|
+
const scriptPath = path.resolve(rawScriptPath);
|
|
40
|
+
const stateDir = path.join(path.dirname(scriptPath), ".scriptctl");
|
|
41
|
+
const name = path.basename(scriptPath);
|
|
42
|
+
return {
|
|
43
|
+
scriptPath,
|
|
44
|
+
stateDir,
|
|
45
|
+
metadataPath: path.join(stateDir, `${name}.working-copy.json`),
|
|
46
|
+
lockPath: path.join(stateDir, name),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export async function withScriptFileLock(rawScriptPath, operation) {
|
|
50
|
+
const paths = workingCopyPaths(rawScriptPath);
|
|
51
|
+
fs.mkdirSync(paths.stateDir, { recursive: true });
|
|
52
|
+
let release = null;
|
|
53
|
+
try {
|
|
54
|
+
release = await lockfile.lock(paths.lockPath, {
|
|
55
|
+
realpath: false,
|
|
56
|
+
stale: 120_000,
|
|
57
|
+
update: 10_000,
|
|
58
|
+
retries: { retries: 60, factor: 1, minTimeout: 500, maxTimeout: 500 },
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
throw new CliError("SCRIPT BLOCKED: working copy is busy", "Timed out waiting for the script file lock.", {
|
|
63
|
+
exitCode: EXIT_RUNTIME,
|
|
64
|
+
errorCode: "WORKING_COPY_LOCK_TIMEOUT",
|
|
65
|
+
received: [paths.scriptPath, error instanceof Error ? error.message : String(error)],
|
|
66
|
+
nextSteps: ["Wait for the other scriptctl command to finish, then retry."],
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
return await operation();
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
await release();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function atomicWriteJson(filePath, value) {
|
|
77
|
+
const dir = path.dirname(filePath);
|
|
78
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
79
|
+
const tempPath = path.join(dir, `.${path.basename(filePath)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
80
|
+
const descriptor = fs.openSync(tempPath, "wx");
|
|
81
|
+
try {
|
|
82
|
+
fs.writeFileSync(descriptor, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
83
|
+
fs.fsyncSync(descriptor);
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
fs.closeSync(descriptor);
|
|
87
|
+
}
|
|
88
|
+
fs.renameSync(tempPath, filePath);
|
|
89
|
+
}
|
|
90
|
+
export function readWorkingCopyMetadata(rawScriptPath) {
|
|
91
|
+
const metadataPath = workingCopyPaths(rawScriptPath).metadataPath;
|
|
92
|
+
if (!fs.existsSync(metadataPath))
|
|
93
|
+
return null;
|
|
94
|
+
let value;
|
|
95
|
+
try {
|
|
96
|
+
value = JSON.parse(fs.readFileSync(metadataPath, "utf-8"));
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
throw invalidMetadata(metadataPath, error instanceof Error ? error.message : String(error));
|
|
100
|
+
}
|
|
101
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
102
|
+
throw invalidMetadata(metadataPath, "metadata root must be an object");
|
|
103
|
+
}
|
|
104
|
+
const record = value;
|
|
105
|
+
if (record.schemaVersion !== 1
|
|
106
|
+
|| typeof record.projectGroupNo !== "string" || record.projectGroupNo.length === 0
|
|
107
|
+
|| typeof record.apiBaseUrl !== "string" || record.apiBaseUrl.length === 0
|
|
108
|
+
|| !Number.isInteger(record.baseRevision) || Number(record.baseRevision) < 0
|
|
109
|
+
|| typeof record.baseContentHash !== "string" || !record.baseContentHash.startsWith("sha256:")) {
|
|
110
|
+
throw invalidMetadata(metadataPath, "required fields are missing or invalid");
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
schemaVersion: 1,
|
|
114
|
+
projectGroupNo: record.projectGroupNo,
|
|
115
|
+
apiBaseUrl: record.apiBaseUrl,
|
|
116
|
+
baseRevision: Number(record.baseRevision),
|
|
117
|
+
baseContentHash: record.baseContentHash,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
export function writeWorkingCopyMetadata(rawScriptPath, metadata) {
|
|
121
|
+
atomicWriteJson(workingCopyPaths(rawScriptPath).metadataPath, metadata);
|
|
122
|
+
}
|
|
123
|
+
function invalidMetadata(metadataPath, reason) {
|
|
124
|
+
return new CliError("SCRIPT BLOCKED: working copy metadata invalid", "Working copy metadata is invalid.", {
|
|
125
|
+
exitCode: EXIT_INPUT,
|
|
126
|
+
errorCode: "WORKING_COPY_METADATA_INVALID",
|
|
127
|
+
required: ["valid scriptctl working-copy metadata"],
|
|
128
|
+
received: [metadataPath, reason],
|
|
129
|
+
nextSteps: ["Run `scriptctl checkout --discard-local` to recreate the working copy metadata."],
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
export function assertScriptUnchanged(scriptPath, expectedHash) {
|
|
133
|
+
let current;
|
|
134
|
+
try {
|
|
135
|
+
current = JSON.parse(fs.readFileSync(scriptPath, "utf-8"));
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw new CliError("SCRIPT BLOCKED: local script changed", "The local script changed while the command was running.", {
|
|
139
|
+
exitCode: EXIT_INPUT,
|
|
140
|
+
errorCode: "LOCAL_SCRIPT_CHANGED",
|
|
141
|
+
received: [scriptPath, error instanceof Error ? error.message : String(error)],
|
|
142
|
+
nextSteps: ["Read the latest local script and re-apply the operation."],
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
const currentHash = semanticScriptHash(current);
|
|
146
|
+
if (currentHash !== expectedHash) {
|
|
147
|
+
throw new CliError("SCRIPT BLOCKED: local script changed", "The local script changed while the command was running.", {
|
|
148
|
+
exitCode: EXIT_INPUT,
|
|
149
|
+
errorCode: "LOCAL_SCRIPT_CHANGED",
|
|
150
|
+
received: [`loadedHash=${expectedHash}`, `currentHash=${currentHash}`, scriptPath],
|
|
151
|
+
nextSteps: ["Read the latest local script and re-apply the operation."],
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=script-working-copy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script-working-copy.js","sourceRoot":"","sources":["../../src/infra/script-working-copy.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,77 +1,4 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
// dependency on direct.
|
|
5
|
-
import * as path from "node:path";
|
|
6
|
-
import { CliError, EXIT_INPUT, EXIT_OK, sha256Text } from "../../common.js";
|
|
7
|
-
import { validateScriptV3 } from "../../domain/ingest/validation.js";
|
|
8
|
-
import { WorkspaceStore } from "../../infra/ingest/workspace-store.js";
|
|
9
|
-
import { LocalScriptOutputStore } from "../../infra/local-script-output-store.js";
|
|
10
|
-
import { remoteScriptOutputStoreFromEnv } from "../../infra/script-output-api.js";
|
|
11
|
-
import { ScriptOutputApiError, resolveOutputModeOrThrow } from "../../infra/script-output-store.js";
|
|
12
|
-
const strOf = (v) => (v === null || v === undefined ? "" : String(v));
|
|
13
|
-
function makeOutputStore(opts) {
|
|
14
|
-
const mode = resolveOutputModeOrThrow({ remote: opts["remote"], local: opts["local"], projectGroupNo: opts["project_group_no"] });
|
|
15
|
-
try {
|
|
16
|
-
return mode === "local"
|
|
17
|
-
? LocalScriptOutputStore.fromEnv(strOf(opts["workspace_path"]).trim() || undefined)
|
|
18
|
-
: remoteScriptOutputStoreFromEnv(strOf(opts["project_group_no"]).trim() || null);
|
|
19
|
-
}
|
|
20
|
-
catch (exc) {
|
|
21
|
-
throw new CliError("PUBLISH BLOCKED: output not configured", "Output store not configured.", {
|
|
22
|
-
exitCode: EXIT_INPUT,
|
|
23
|
-
errorCode: "SCRIPT_API_CONFIG_MISSING",
|
|
24
|
-
required: [mode === "local" ? "SCRIPTCTL_OUTPUT_DIR" : "gateway config (AWB_BASE_URL / ACCESS_KEY / project-group-no)"],
|
|
25
|
-
received: [exc instanceof Error ? exc.message : String(exc)],
|
|
26
|
-
nextSteps: ["Run `scriptctl doctor` to check output configuration."],
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
async function currentRevisionOrZero(client) {
|
|
31
|
-
try {
|
|
32
|
-
const revision = await client.getRevision();
|
|
33
|
-
return revision ? Number(revision["revision"] ?? 0) : 0;
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
return 0;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
export async function commandPublish(opts) {
|
|
40
|
-
const workspacePath = strOf(opts["workspace_path"] || "workspace");
|
|
41
|
-
const store = new WorkspaceStore(path.resolve(workspacePath));
|
|
42
|
-
if (!store.has("script.json")) {
|
|
43
|
-
throw new CliError("PUBLISH BLOCKED: no script.json", "workspace has no script.json to publish.", {
|
|
44
|
-
exitCode: EXIT_INPUT, errorCode: "PUBLISH_NO_SCRIPT",
|
|
45
|
-
received: [store.path("script.json")],
|
|
46
|
-
nextSteps: ["Run `scriptctl ingest` first."],
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
const script = store.readJson("script.json");
|
|
50
|
-
const validation = validateScriptV3(script);
|
|
51
|
-
if (!validation.ok) {
|
|
52
|
-
throw new CliError("PUBLISH BLOCKED: v3 validation failed", "script.json does not pass v3 validation.", {
|
|
53
|
-
exitCode: EXIT_INPUT, errorCode: "PUBLISH_VALIDATION_FAILED",
|
|
54
|
-
received: validation.errors.slice(0, 5).map((e) => `${e.path}: ${e.message}`),
|
|
55
|
-
nextSteps: ["Re-run `scriptctl ingest` to regenerate a valid script.json."],
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
const client = makeOutputStore(opts);
|
|
59
|
-
const baseRevision = await currentRevisionOrZero(client);
|
|
60
|
-
const requestId = strOf(opts["request_id"]).trim() || `scriptctl-ingest:${sha256Text(JSON.stringify(script))}`;
|
|
61
|
-
let res;
|
|
62
|
-
try {
|
|
63
|
-
res = await client.replaceScript({ requestId, baseRevision, script, source: "ctl" });
|
|
64
|
-
}
|
|
65
|
-
catch (exc) {
|
|
66
|
-
if (exc instanceof ScriptOutputApiError) {
|
|
67
|
-
const conflict = /版本冲突|revision/i.test(exc.message);
|
|
68
|
-
throw new CliError("PUBLISH BLOCKED: write failed", conflict ? "Script updated by another revision; reload and retry." : exc.message, {
|
|
69
|
-
exitCode: EXIT_INPUT, errorCode: conflict ? "SCRIPT_REVISION_CONFLICT" : "SCRIPT_API_WRITE_FAILED",
|
|
70
|
-
received: [exc.message], nextSteps: conflict ? ["Re-run publish after reload."] : ["Check output config and retry."],
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
throw exc;
|
|
74
|
-
}
|
|
75
|
-
return [{ result: [`published rev ${baseRevision} → ${res["revision"]}; idempotent ${res["idempotent"] ?? false}`] }, EXIT_OK];
|
|
76
|
-
}
|
|
1
|
+
// Kept as an import-compatible name for internal callers. The public publish
|
|
2
|
+
// implementation has one source of truth: the managed local working-copy flow.
|
|
3
|
+
export { commandWorkingCopyPublish as commandPublish } from "../script/working-copy.js";
|
|
77
4
|
//# sourceMappingURL=publish-command.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish-command.js","sourceRoot":"","sources":["../../../src/usecases/ingest/publish-command.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"publish-command.js","sourceRoot":"","sources":["../../../src/usecases/ingest/publish-command.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,+EAA+E;AAC/E,OAAO,EAAE,yBAAyB,IAAI,cAAc,EAAE,MAAM,2BAA2B,CAAC"}
|
|
@@ -1,64 +1,28 @@
|
|
|
1
|
-
import { CliError, EXIT_INPUT, EXIT_NEEDS_AGENT, EXIT_OK, exists,
|
|
1
|
+
import { CliError, EXIT_INPUT, EXIT_NEEDS_AGENT, EXIT_OK, exists, scriptJsonPath, } from "../../common.js";
|
|
2
|
+
import { atomicWriteJson } from "../../infra/script-working-copy.js";
|
|
2
3
|
import { blankScript } from "../../domain/script/lookups.js";
|
|
3
|
-
import { ScriptOutputApiError, } from "../../infra/script-output-store.js";
|
|
4
4
|
import { strOf, } from "../../domain/script/shared.js";
|
|
5
|
-
import { ScriptEditSession,
|
|
5
|
+
import { ScriptEditSession, validateSession, } from "./session.js";
|
|
6
6
|
import { blankCreateReport, } from "./lib.js";
|
|
7
7
|
export async function commandCreate(opts) {
|
|
8
8
|
const workspace = strOf(opts["workspace_path"] || "workspace");
|
|
9
9
|
const force = Boolean(opts["force"]);
|
|
10
10
|
const title = strOf(opts["title"]).trim();
|
|
11
11
|
const script = blankScript(title);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
assertTargetCoherent(opts);
|
|
16
|
-
const localPath = strOf(opts["script_path"]).trim() || null;
|
|
17
|
-
if (localPath) {
|
|
18
|
-
if (exists(localPath) && !force) {
|
|
19
|
-
throw new CliError("CREATE BLOCKED: target already exists", "A script already exists at the target.", {
|
|
20
|
-
exitCode: EXIT_INPUT,
|
|
21
|
-
required: ["a non-existent target, or --force to overwrite"],
|
|
22
|
-
received: [localPath],
|
|
23
|
-
nextSteps: ["Edit the existing script with the atomic verbs, or pass --force to overwrite with a blank scaffold."],
|
|
24
|
-
errorCode: "SCRIPT_EXISTS",
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
writeJson(localPath, script);
|
|
28
|
-
const session = new ScriptEditSession({ workspace, scriptPath: localPath, script });
|
|
29
|
-
const validation = validateSession(session);
|
|
30
|
-
const passed = Boolean(validation["passed"]);
|
|
31
|
-
return [blankCreateReport(session, validation, null), passed ? EXIT_OK : EXIT_NEEDS_AGENT];
|
|
32
|
-
}
|
|
33
|
-
// DB mode: first write must be at revision 0. We never silently overwrite a
|
|
34
|
-
// populated DB script with a blank one — that would destroy work.
|
|
35
|
-
const client = scriptOutputClient(opts);
|
|
36
|
-
const baseRevision = await currentRevisionOrZero(client);
|
|
37
|
-
if (baseRevision > 0) {
|
|
38
|
-
throw new CliError("CREATE BLOCKED: DB script already exists", "A script already exists for this project.", {
|
|
12
|
+
const localPath = scriptJsonPath(opts);
|
|
13
|
+
if (exists(localPath) && !force) {
|
|
14
|
+
throw new CliError("CREATE BLOCKED: target already exists", "A script already exists at the target.", {
|
|
39
15
|
exitCode: EXIT_INPUT,
|
|
40
|
-
required: ["
|
|
41
|
-
received: [
|
|
42
|
-
nextSteps: ["Edit the existing script with the atomic verbs
|
|
16
|
+
required: ["a non-existent target, or --force to overwrite"],
|
|
17
|
+
received: [localPath],
|
|
18
|
+
nextSteps: ["Edit the existing script with the atomic verbs, or pass --force to overwrite with a blank scaffold."],
|
|
43
19
|
errorCode: "SCRIPT_EXISTS",
|
|
44
20
|
});
|
|
45
21
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
res = await client.replaceScript({ requestId, baseRevision, script, source: "ctl" });
|
|
50
|
-
}
|
|
51
|
-
catch (exc) {
|
|
52
|
-
if (exc instanceof ScriptOutputApiError) {
|
|
53
|
-
throw apiErrorToCli("SCRIPT API BLOCKED: Create write failed", exc);
|
|
54
|
-
}
|
|
55
|
-
throw exc;
|
|
56
|
-
}
|
|
57
|
-
const revision = Number(res["revision"] ?? 0);
|
|
58
|
-
const session = new ScriptEditSession({ workspace, script, client, projectGroupNo: client.projectGroupNo, revision });
|
|
22
|
+
atomicWriteJson(localPath, script);
|
|
23
|
+
const session = new ScriptEditSession({ workspace, scriptPath: localPath, script });
|
|
59
24
|
const validation = validateSession(session);
|
|
60
|
-
await syncValidationResult(session, validation, revision);
|
|
61
25
|
const passed = Boolean(validation["passed"]);
|
|
62
|
-
return [blankCreateReport(session, validation,
|
|
26
|
+
return [blankCreateReport(session, validation, null), passed ? EXIT_OK : EXIT_NEEDS_AGENT];
|
|
63
27
|
}
|
|
64
28
|
//# sourceMappingURL=create.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/usecases/script/create.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,QAAQ,EACR,UAAU,EACV,gBAAgB,EAChB,OAAO,EAIP,MAAM,
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/usecases/script/create.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,QAAQ,EACR,UAAU,EACV,gBAAgB,EAChB,OAAO,EAIP,MAAM,EAGN,cAAc,GAGf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAErE,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AA2B7D,OAAO,EAKL,KAAK,GACN,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,iBAAiB,EACE,eAAe,GAEnC,MAAM,cAAc,CAAC;AACtB,OAAO,EACib,iBAAiB,GACxc,MAAM,UAAU,CAAC;AAElB,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAU;IAC5C,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,WAAW,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAElC,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAChC,MAAM,IAAI,QAAQ,CAAC,uCAAuC,EAAE,wCAAwC,EAAE;YACpG,QAAQ,EAAE,UAAU;YACpB,QAAQ,EAAE,CAAC,gDAAgD,CAAC;YAC5D,QAAQ,EAAE,CAAC,SAAS,CAAC;YACrB,SAAS,EAAE,CAAC,qGAAqG,CAAC;YAClH,SAAS,EAAE,eAAe;SAC3B,CAAC,CAAC;IACL,CAAC;IACD,eAAe,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IACpF,MAAM,UAAU,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;AAC7F,CAAC"}
|
|
@@ -12,6 +12,7 @@ export declare class ScriptEditSession {
|
|
|
12
12
|
workspace: string;
|
|
13
13
|
script: Dict;
|
|
14
14
|
readonly baseline: Dict;
|
|
15
|
+
readonly baselineHash: string;
|
|
15
16
|
scriptPath: string | null;
|
|
16
17
|
client: ScriptOutputStore | null;
|
|
17
18
|
projectGroupNo: string | null;
|
|
@@ -51,7 +52,7 @@ export declare function syncValidationResult(session: ScriptEditSession, validat
|
|
|
51
52
|
export declare function requestIdForScriptWrite(opts: Dict, op: string, payload: {
|
|
52
53
|
script: Dict;
|
|
53
54
|
}): string;
|
|
54
|
-
export declare function saveScriptSession(session: ScriptEditSession,
|
|
55
|
+
export declare function saveScriptSession(session: ScriptEditSession, _opts: Dict, _op: string): Promise<[number | null, boolean]>;
|
|
55
56
|
export declare function scriptSourceText(workspace: string): string;
|
|
56
57
|
export declare function patchOperationsFromPayload(payload: unknown): Dict[];
|
|
57
58
|
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
-
import { CliError, EXIT_INPUT, EXIT_RUNTIME, EXIT_USAGE, exists, readJson, readText, scriptJsonPath, sha256Text,
|
|
2
|
+
import { CliError, EXIT_INPUT, EXIT_RUNTIME, EXIT_USAGE, exists, readJson, readText, scriptJsonPath, sha256Text, } from "../../common.js";
|
|
3
3
|
import { validateScriptV3 } from "../../domain/ingest/validation.js";
|
|
4
4
|
import { remoteScriptOutputStoreFromEnv } from "../../infra/script-output-api.js";
|
|
5
|
+
import { assertScriptUnchanged, atomicWriteJson, semanticScriptHash, } from "../../infra/script-working-copy.js";
|
|
5
6
|
import { LocalScriptOutputStore } from "../../infra/local-script-output-store.js";
|
|
6
7
|
import { ScriptOutputApiError, resolveOutputModeOrThrow, } from "../../infra/script-output-store.js";
|
|
7
8
|
import { sortDeep } from "../../domain/script/text-util.js";
|
|
@@ -54,6 +55,7 @@ export class ScriptEditSession {
|
|
|
54
55
|
// on save (patchScript). V1/local stores ignore it and replace the whole
|
|
55
56
|
// document, so the snapshot is harmless overhead there.
|
|
56
57
|
baseline;
|
|
58
|
+
baselineHash;
|
|
57
59
|
scriptPath;
|
|
58
60
|
client;
|
|
59
61
|
projectGroupNo;
|
|
@@ -62,6 +64,7 @@ export class ScriptEditSession {
|
|
|
62
64
|
this.workspace = opts.workspace;
|
|
63
65
|
this.script = opts.script;
|
|
64
66
|
this.baseline = structuredClone(opts.script);
|
|
67
|
+
this.baselineHash = semanticScriptHash(opts.script);
|
|
65
68
|
this.scriptPath = opts.scriptPath ?? null;
|
|
66
69
|
this.client = opts.client ?? null;
|
|
67
70
|
this.projectGroupNo = opts.projectGroupNo ?? null;
|
|
@@ -225,12 +228,12 @@ export function apiErrorToCli(title, exc) {
|
|
|
225
228
|
// text like "revision" or "not found" can leak in from upstream proxies,
|
|
226
229
|
// DNS errors ("name not found"), or HTTP 404 routing mismatches — matching on
|
|
227
230
|
// those substrings would misroute auth/network failures into the conflict /
|
|
228
|
-
// missing-script UX. The
|
|
229
|
-
// keyword
|
|
230
|
-
const isConflict = rawMessage.includes("版本冲突");
|
|
231
|
+
// missing-script UX. The current CAS endpoint uses code 9002; the Chinese
|
|
232
|
+
// keyword keeps the retained legacy endpoint compatible.
|
|
233
|
+
const isConflict = String(exc.code) === "9002" || rawMessage.includes("版本冲突");
|
|
231
234
|
const isMissing = rawMessage.includes("剧本不存在");
|
|
232
235
|
const publicMessage = isConflict
|
|
233
|
-
? "Script has been updated by another revision;
|
|
236
|
+
? "Script has been updated by another revision; keep local work and merge before retrying."
|
|
234
237
|
: isMissing
|
|
235
238
|
? "Script not found on the gateway."
|
|
236
239
|
: "Gateway request failed.";
|
|
@@ -240,13 +243,13 @@ export function apiErrorToCli(title, exc) {
|
|
|
240
243
|
received: [rawMessage, `status: ${exc.status ?? "<unknown>"}`],
|
|
241
244
|
nextSteps: [
|
|
242
245
|
isConflict
|
|
243
|
-
? "
|
|
246
|
+
? "Keep the local file, inspect the latest remote snapshot, and merge before publishing again."
|
|
244
247
|
: isMissing
|
|
245
248
|
? "Run `scriptctl publish` to publish a script first."
|
|
246
249
|
: "Run `scriptctl doctor` to verify gateway configuration; retry if it was transient.",
|
|
247
250
|
],
|
|
248
251
|
errorCode: isConflict
|
|
249
|
-
? "
|
|
252
|
+
? "REVISION_CONFLICT"
|
|
250
253
|
: isMissing
|
|
251
254
|
? "SCRIPT_NOT_FOUND"
|
|
252
255
|
: "SCRIPT_API_FAILED",
|
|
@@ -308,22 +311,13 @@ export async function loadRemoteScript(opts, workspace) {
|
|
|
308
311
|
}
|
|
309
312
|
export async function loadScriptForEdit(opts) {
|
|
310
313
|
const workspace = strOf(opts["workspace_path"] || "workspace");
|
|
311
|
-
// Single conflict gate (shared with commandCreate): rejects any contradictory
|
|
312
|
-
// mix of --script-path / --remote / --local / --project-group-no BEFORE we
|
|
313
|
-
// branch file-vs-store, so a store flag can never be silently dropped on the
|
|
314
|
-
// local-file path.
|
|
315
|
-
assertTargetCoherent(opts);
|
|
316
|
-
// No local file selected → the store (remote/local) is the target.
|
|
317
|
-
if (!opts["script_path"]) {
|
|
318
|
-
return loadRemoteScript(opts, workspace);
|
|
319
|
-
}
|
|
320
314
|
const p = scriptJsonPath(opts);
|
|
321
315
|
if (!exists(p)) {
|
|
322
316
|
throw new CliError("SCRIPT BLOCKED: Local script file not found", "Local script file not found.", {
|
|
323
317
|
exitCode: EXIT_INPUT,
|
|
324
|
-
required: ["
|
|
318
|
+
required: ["existing local script JSON"],
|
|
325
319
|
received: [p],
|
|
326
|
-
nextSteps: ["
|
|
320
|
+
nextSteps: ["Run `scriptctl checkout`, `scriptctl create`, or pass --script-path to an existing script."],
|
|
327
321
|
errorCode: "SCRIPT_NOT_FOUND",
|
|
328
322
|
});
|
|
329
323
|
}
|
|
@@ -479,37 +473,12 @@ export function requestIdForScriptWrite(opts, op, payload) {
|
|
|
479
473
|
}));
|
|
480
474
|
return `scriptctl:${op}:${sha256Text(canonical)}`;
|
|
481
475
|
}
|
|
482
|
-
export async function saveScriptSession(session,
|
|
483
|
-
if (
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
throw new Error("remote script session missing client");
|
|
489
|
-
const baseRevision = Number(session.revision ?? 0);
|
|
490
|
-
const requestId = requestIdForScriptWrite(opts, op, { script: session.script });
|
|
491
|
-
let res;
|
|
492
|
-
try {
|
|
493
|
-
// Incremental save: V2 stores compute compare(baseline, script) and send an
|
|
494
|
-
// RFC6902 PATCH; V1/local stores degrade patchScript to a whole-document
|
|
495
|
-
// replace. The request id and baseRevision contract are unchanged, so the
|
|
496
|
-
// idempotency/conflict behaviour is identical to the prior replaceScript.
|
|
497
|
-
res = await session.client.patchScript({
|
|
498
|
-
requestId,
|
|
499
|
-
baseRevision,
|
|
500
|
-
old: session.baseline,
|
|
501
|
-
edited: session.script,
|
|
502
|
-
source: "ctl",
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
catch (exc) {
|
|
506
|
-
if (exc instanceof ScriptOutputApiError) {
|
|
507
|
-
throw apiErrorToCli("SCRIPT API BLOCKED: Script write failed", exc);
|
|
508
|
-
}
|
|
509
|
-
throw exc;
|
|
510
|
-
}
|
|
511
|
-
session.revision = Number(res["revision"] ?? session.revision ?? 0);
|
|
512
|
-
return [session.revision, Boolean(res["idempotent"])];
|
|
476
|
+
export async function saveScriptSession(session, _opts, _op) {
|
|
477
|
+
if (session.scriptPath === null)
|
|
478
|
+
throw new Error("local script session missing script path");
|
|
479
|
+
assertScriptUnchanged(session.scriptPath, session.baselineHash);
|
|
480
|
+
atomicWriteJson(session.scriptPath, session.script);
|
|
481
|
+
return [null, false];
|
|
513
482
|
}
|
|
514
483
|
export function scriptSourceText(workspace) {
|
|
515
484
|
const sourcePath = path.join(workspace, "source.txt");
|