@intra-mart/accel 0.1.0-dev-20260420
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/assets/assets.tar.gz +0 -0
- package/dist/asset/default-source.d.ts +1 -0
- package/dist/asset/default-source.js +9 -0
- package/dist/asset/deployer.d.ts +14 -0
- package/dist/asset/deployer.js +109 -0
- package/dist/asset/file-provider.d.ts +2 -0
- package/dist/asset/file-provider.js +25 -0
- package/dist/asset/local-provider.d.ts +2 -0
- package/dist/asset/local-provider.js +45 -0
- package/dist/asset/regex-replacement.d.ts +2 -0
- package/dist/asset/regex-replacement.js +12 -0
- package/dist/asset/types.d.ts +1 -0
- package/dist/asset/types.js +1 -0
- package/dist/asset/walker.d.ts +10 -0
- package/dist/asset/walker.js +165 -0
- package/dist/commands/attach.d.ts +64 -0
- package/dist/commands/attach.js +163 -0
- package/dist/commands/detach.d.ts +1 -0
- package/dist/commands/detach.js +74 -0
- package/dist/commands/init.d.ts +70 -0
- package/dist/commands/init.js +178 -0
- package/dist/core/condition-evaluator.d.ts +2 -0
- package/dist/core/condition-evaluator.js +26 -0
- package/dist/core/constants.d.ts +11 -0
- package/dist/core/constants.js +30 -0
- package/dist/core/module-map.d.ts +7 -0
- package/dist/core/module-map.js +12 -0
- package/dist/core/types.d.ts +100 -0
- package/dist/core/types.js +8 -0
- package/dist/core/variable-interpolator.d.ts +10 -0
- package/dist/core/variable-interpolator.js +37 -0
- package/dist/core/version-map.d.ts +7 -0
- package/dist/core/version-map.js +45 -0
- package/dist/i18n/en.d.ts +1 -0
- package/dist/i18n/en.js +47 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.js +19 -0
- package/dist/i18n/ja.d.ts +1 -0
- package/dist/i18n/ja.js +47 -0
- package/dist/i18n/zh_CN.d.ts +1 -0
- package/dist/i18n/zh_CN.js +47 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/interactive/agent-detect.d.ts +8 -0
- package/dist/interactive/agent-detect.js +31 -0
- package/dist/interactive/prompts.d.ts +22 -0
- package/dist/interactive/prompts.js +226 -0
- package/dist/juggling/extractor.d.ts +4 -0
- package/dist/juggling/extractor.js +15 -0
- package/dist/juggling/parser.d.ts +13 -0
- package/dist/juggling/parser.js +66 -0
- package/dist/markdown/processor.d.ts +3 -0
- package/dist/markdown/processor.js +16 -0
- package/dist/markdown/section-replacement.d.ts +2 -0
- package/dist/markdown/section-replacement.js +68 -0
- package/dist/markdown/text-replacement.d.ts +8 -0
- package/dist/markdown/text-replacement.js +32 -0
- package/dist/utils/archive.d.ts +3 -0
- package/dist/utils/archive.js +20 -0
- package/dist/utils/hash.d.ts +1 -0
- package/dist/utils/hash.js +6 -0
- package/dist/utils/locale-detect.d.ts +2 -0
- package/dist/utils/locale-detect.js +35 -0
- package/dist/utils/settings-io.d.ts +7 -0
- package/dist/utils/settings-io.js +54 -0
- package/package.json +60 -0
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const defaultAssetSourcePath: () => string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
// Layout assumption (both dev and published package):
|
|
4
|
+
// <root>/dist/asset/default-source.js (or <root>/src/asset/default-source.ts in dev)
|
|
5
|
+
// <root>/assets/assets.tar.gz
|
|
6
|
+
export const defaultAssetSourcePath = () => {
|
|
7
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
return join(here, "..", "..", "assets", "assets.tar.gz");
|
|
9
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AssetProvider, AccelSettings, HashsumEntry } from "../core/types.js";
|
|
2
|
+
export type DeployOptions = {
|
|
3
|
+
projectDir: string;
|
|
4
|
+
settings: AccelSettings;
|
|
5
|
+
provider: AssetProvider;
|
|
6
|
+
noInstall: boolean;
|
|
7
|
+
skipExistingFiles?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type DeployResult = {
|
|
10
|
+
deployedFiles: string[];
|
|
11
|
+
hashEntries: HashsumEntry[];
|
|
12
|
+
skippedFiles: string[];
|
|
13
|
+
};
|
|
14
|
+
export declare const deployAssets: (options: DeployOptions) => Promise<DeployResult>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir, stat } from "node:fs/promises";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { labelToSemver } from "../core/version-map.js";
|
|
4
|
+
import { walkAssetRepo } from "./walker.js";
|
|
5
|
+
import { processMarkdown } from "../markdown/processor.js";
|
|
6
|
+
import { applyRegexReplacements } from "./regex-replacement.js";
|
|
7
|
+
import { computeSha1 } from "../utils/hash.js";
|
|
8
|
+
import { writeSettings, writeHashsum } from "../utils/settings-io.js";
|
|
9
|
+
import { execSync } from "node:child_process";
|
|
10
|
+
const fileExists = async (path) => {
|
|
11
|
+
try {
|
|
12
|
+
await stat(path);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const collectReplacements = (entry) => {
|
|
20
|
+
if (!entry.meta?.replacements)
|
|
21
|
+
return [];
|
|
22
|
+
return entry.meta.replacements;
|
|
23
|
+
};
|
|
24
|
+
export const deployAssets = async (options) => {
|
|
25
|
+
const { projectDir, settings, provider, noInstall, skipExistingFiles } = options;
|
|
26
|
+
try {
|
|
27
|
+
// 1. Fetch assets
|
|
28
|
+
const repoDir = await provider.fetch();
|
|
29
|
+
// 2. Build eval context
|
|
30
|
+
const semver = labelToSemver(settings.accelplatformVersion);
|
|
31
|
+
const context = {
|
|
32
|
+
version: semver,
|
|
33
|
+
modules: settings.modules,
|
|
34
|
+
locale: settings.locale,
|
|
35
|
+
agents: settings.agents,
|
|
36
|
+
name: settings.name,
|
|
37
|
+
group: settings.group,
|
|
38
|
+
description: settings.description,
|
|
39
|
+
accelplatformVersion: settings.accelplatformVersion,
|
|
40
|
+
database: settings.database,
|
|
41
|
+
projectVersion: settings.projectVersion,
|
|
42
|
+
};
|
|
43
|
+
// 3. Walk and filter
|
|
44
|
+
const entries = await walkAssetRepo(repoDir, semver, context);
|
|
45
|
+
// 4. Deploy files
|
|
46
|
+
const deployedFiles = [];
|
|
47
|
+
const hashEntries = [];
|
|
48
|
+
const deployedAssets = {};
|
|
49
|
+
const skippedFiles = [];
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
const targetPath = entry.relativePath;
|
|
52
|
+
const destPath = join(projectDir, targetPath);
|
|
53
|
+
if (skipExistingFiles && (await fileExists(destPath))) {
|
|
54
|
+
skippedFiles.push(targetPath);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
// Read source file
|
|
58
|
+
let content = await readFile(entry.sourcePath, "utf-8");
|
|
59
|
+
// Apply replacements
|
|
60
|
+
const replacements = collectReplacements(entry);
|
|
61
|
+
if (replacements.length > 0) {
|
|
62
|
+
if (targetPath.endsWith(".md")) {
|
|
63
|
+
// Markdown: process with remark AST
|
|
64
|
+
content = processMarkdown(content, replacements, context);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Non-markdown: apply regex replacements only
|
|
68
|
+
const regexReplacements = replacements.filter((r) => r.type === "regex");
|
|
69
|
+
if (regexReplacements.length > 0) {
|
|
70
|
+
content = applyRegexReplacements(content, regexReplacements, context);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Write to project directory
|
|
75
|
+
await mkdir(dirname(destPath), { recursive: true });
|
|
76
|
+
await writeFile(destPath, content, "utf-8");
|
|
77
|
+
// Record hash
|
|
78
|
+
const sha1 = computeSha1(content);
|
|
79
|
+
const version = entry.meta?.version ?? "0.0.0";
|
|
80
|
+
hashEntries.push({ filePath: targetPath, version, sha1 });
|
|
81
|
+
deployedAssets[targetPath] = version;
|
|
82
|
+
deployedFiles.push(targetPath);
|
|
83
|
+
}
|
|
84
|
+
// 5. Write settings and hashsum
|
|
85
|
+
const updatedSettings = {
|
|
86
|
+
...settings,
|
|
87
|
+
deployedAssets,
|
|
88
|
+
};
|
|
89
|
+
await writeSettings(projectDir, updatedSettings);
|
|
90
|
+
await writeHashsum(projectDir, hashEntries);
|
|
91
|
+
// 6. Run install commands at project root
|
|
92
|
+
if (!noInstall) {
|
|
93
|
+
const installCmds = ["bun install", "bun run build"];
|
|
94
|
+
for (const cmd of installCmds) {
|
|
95
|
+
try {
|
|
96
|
+
execSync(cmd, { cwd: projectDir, stdio: "pipe" });
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
console.warn(`Warning: install command failed: ${cmd} in ${projectDir}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { deployedFiles, hashEntries, skippedFiles };
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
// 7. Cleanup (delegated to provider)
|
|
107
|
+
await provider.cleanup().catch(() => { });
|
|
108
|
+
}
|
|
109
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { stat, mkdir, rm } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { extractTarGz, createTempDir } from "../utils/archive.js";
|
|
4
|
+
export const createFileAssetProvider = (sourcePath) => {
|
|
5
|
+
let tmpDir = null;
|
|
6
|
+
return {
|
|
7
|
+
fetch: async () => {
|
|
8
|
+
const st = await stat(sourcePath);
|
|
9
|
+
if (st.isDirectory()) {
|
|
10
|
+
return sourcePath;
|
|
11
|
+
}
|
|
12
|
+
tmpDir = await createTempDir("accel-assets-");
|
|
13
|
+
const extractDir = join(tmpDir, "repo");
|
|
14
|
+
await mkdir(extractDir, { recursive: true });
|
|
15
|
+
await extractTarGz(sourcePath, extractDir);
|
|
16
|
+
return extractDir;
|
|
17
|
+
},
|
|
18
|
+
cleanup: async () => {
|
|
19
|
+
if (tmpDir) {
|
|
20
|
+
await rm(tmpDir, { recursive: true, force: true }).catch(() => { });
|
|
21
|
+
tmpDir = null;
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { get } from "node:http";
|
|
2
|
+
import { createWriteStream } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { mkdir, rm } from "node:fs/promises";
|
|
5
|
+
import { extractTarGz, createTempDir } from "../utils/archive.js";
|
|
6
|
+
const downloadFile = (url, destPath) => {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
const file = createWriteStream(destPath);
|
|
9
|
+
get(url, (response) => {
|
|
10
|
+
if (response.statusCode !== 200) {
|
|
11
|
+
file.close();
|
|
12
|
+
reject(new Error(`HTTP ${response.statusCode} when downloading from ${url}`));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
response.pipe(file);
|
|
16
|
+
file.on("finish", () => {
|
|
17
|
+
file.close();
|
|
18
|
+
resolve();
|
|
19
|
+
});
|
|
20
|
+
}).on("error", (err) => {
|
|
21
|
+
file.close();
|
|
22
|
+
reject(err);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
export const createLocalAssetProvider = (serverUrl) => {
|
|
27
|
+
let tmpDir = null;
|
|
28
|
+
return {
|
|
29
|
+
fetch: async () => {
|
|
30
|
+
tmpDir = await createTempDir("accel-assets-");
|
|
31
|
+
const archivePath = join(tmpDir, "assets.tar.gz");
|
|
32
|
+
const extractDir = join(tmpDir, "repo");
|
|
33
|
+
await mkdir(extractDir, { recursive: true });
|
|
34
|
+
await downloadFile(`${serverUrl}/archive`, archivePath);
|
|
35
|
+
await extractTarGz(archivePath, extractDir);
|
|
36
|
+
return extractDir;
|
|
37
|
+
},
|
|
38
|
+
cleanup: async () => {
|
|
39
|
+
if (tmpDir) {
|
|
40
|
+
await rm(tmpDir, { recursive: true, force: true }).catch(() => { });
|
|
41
|
+
tmpDir = null;
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { evaluateCondition } from "../core/condition-evaluator.js";
|
|
2
|
+
import { interpolateVariables } from "../core/variable-interpolator.js";
|
|
3
|
+
export const applyRegexReplacements = (content, replacements, context) => {
|
|
4
|
+
let result = content;
|
|
5
|
+
for (const rep of replacements) {
|
|
6
|
+
if (evaluateCondition(rep.condition, context)) {
|
|
7
|
+
const interpolated = interpolateVariables(rep.replacement, context);
|
|
8
|
+
result = result.replace(new RegExp(rep.pattern, "g"), interpolated);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { AssetProvider } from "../core/types.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MetaJson, EvalContext } from "../core/types.js";
|
|
2
|
+
export type AssetEntry = {
|
|
3
|
+
/** Absolute path in the extracted repo */
|
|
4
|
+
sourcePath: string;
|
|
5
|
+
/** Relative path for deployment (e.g., ".claude/CLAUDE.md") */
|
|
6
|
+
relativePath: string;
|
|
7
|
+
/** MetaJson from the directory's _meta.json (or parent's) */
|
|
8
|
+
meta: MetaJson | null;
|
|
9
|
+
};
|
|
10
|
+
export declare const walkAssetRepo: (repoDir: string, targetSemver: string, context: EvalContext) => Promise<AssetEntry[]>;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { join, relative, dirname } from "node:path";
|
|
3
|
+
import { evaluateCondition } from "../core/condition-evaluator.js";
|
|
4
|
+
const META_FILE = "_meta.json";
|
|
5
|
+
const readMetaJson = async (dir) => {
|
|
6
|
+
try {
|
|
7
|
+
const content = await readFile(join(dir, META_FILE), "utf-8");
|
|
8
|
+
const parsed = JSON.parse(content);
|
|
9
|
+
// Default type to "directory" for backward compatibility
|
|
10
|
+
if (!parsed.type) {
|
|
11
|
+
parsed.type = "directory";
|
|
12
|
+
}
|
|
13
|
+
return parsed;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Resolve rename rule: evaluate conditions and find the first matching rule.
|
|
21
|
+
* Returns the source file path and target name, or null if no rule matches.
|
|
22
|
+
*/
|
|
23
|
+
const resolveRenameRule = (rules, context, dir) => {
|
|
24
|
+
if (!rules || rules.length === 0)
|
|
25
|
+
return null;
|
|
26
|
+
for (const rule of rules) {
|
|
27
|
+
if (evaluateCondition(rule.condition, context)) {
|
|
28
|
+
return {
|
|
29
|
+
sourcePath: join(dir, rule.from),
|
|
30
|
+
targetName: rule.to,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
};
|
|
36
|
+
const findMatchingVersionDirs = async (repoDir, targetSemver) => {
|
|
37
|
+
const entries = await readdir(repoDir, { withFileTypes: true });
|
|
38
|
+
const matched = [];
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
if (!entry.isDirectory())
|
|
41
|
+
continue;
|
|
42
|
+
const dirPath = join(repoDir, entry.name);
|
|
43
|
+
const meta = await readMetaJson(dirPath);
|
|
44
|
+
if (!meta) {
|
|
45
|
+
// No _meta.json — skip version directory without meta
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (meta.conditions) {
|
|
49
|
+
// Evaluate only the version condition at root level
|
|
50
|
+
const context = {
|
|
51
|
+
version: targetSemver,
|
|
52
|
+
modules: [],
|
|
53
|
+
locale: "",
|
|
54
|
+
agents: [],
|
|
55
|
+
name: "",
|
|
56
|
+
group: "",
|
|
57
|
+
description: "",
|
|
58
|
+
accelplatformVersion: "",
|
|
59
|
+
database: "",
|
|
60
|
+
projectVersion: "",
|
|
61
|
+
};
|
|
62
|
+
if (evaluateCondition(meta.conditions, context)) {
|
|
63
|
+
matched.push(dirPath);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return matched;
|
|
68
|
+
};
|
|
69
|
+
const walkDirectory = async (dir, baseDir, parentMeta, context) => {
|
|
70
|
+
const entries = [];
|
|
71
|
+
const dirEntries = await readdir(dir, { withFileTypes: true });
|
|
72
|
+
// Check if this directory has its own _meta.json
|
|
73
|
+
const ownMeta = await readMetaJson(dir);
|
|
74
|
+
const effectiveMeta = ownMeta ?? parentMeta;
|
|
75
|
+
// If this directory has conditions and they don't match, skip entirely
|
|
76
|
+
if (ownMeta?.conditions) {
|
|
77
|
+
if (!evaluateCondition(ownMeta.conditions, context)) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Handle type: "file" — no recursion into subdirectories
|
|
82
|
+
if (ownMeta?.type === "file") {
|
|
83
|
+
const parentRelative = relative(baseDir, dirname(dir));
|
|
84
|
+
if (ownMeta.rename && ownMeta.rename.length > 0) {
|
|
85
|
+
// With rename: select one source file via first-match-wins, deploy with `to` name
|
|
86
|
+
const resolved = resolveRenameRule(ownMeta.rename, context, dir);
|
|
87
|
+
if (!resolved)
|
|
88
|
+
return [];
|
|
89
|
+
const targetPath = parentRelative
|
|
90
|
+
? join(parentRelative, resolved.targetName)
|
|
91
|
+
: resolved.targetName;
|
|
92
|
+
entries.push({
|
|
93
|
+
sourcePath: resolved.sourcePath,
|
|
94
|
+
relativePath: targetPath,
|
|
95
|
+
meta: effectiveMeta,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Without rename: deploy all files (except _meta.json) with original names
|
|
100
|
+
for (const entry of dirEntries) {
|
|
101
|
+
if (entry.name === META_FILE)
|
|
102
|
+
continue;
|
|
103
|
+
if (!entry.isFile())
|
|
104
|
+
continue;
|
|
105
|
+
const fullPath = join(dir, entry.name);
|
|
106
|
+
const targetPath = parentRelative
|
|
107
|
+
? join(parentRelative, entry.name)
|
|
108
|
+
: entry.name;
|
|
109
|
+
entries.push({
|
|
110
|
+
sourcePath: fullPath,
|
|
111
|
+
relativePath: targetPath,
|
|
112
|
+
meta: effectiveMeta,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return entries;
|
|
117
|
+
}
|
|
118
|
+
// type: "directory" — walk recursively (existing behavior)
|
|
119
|
+
for (const entry of dirEntries) {
|
|
120
|
+
const fullPath = join(dir, entry.name);
|
|
121
|
+
if (entry.name === META_FILE) {
|
|
122
|
+
// Skip _meta.json itself
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (entry.isDirectory()) {
|
|
126
|
+
const children = await walkDirectory(fullPath, baseDir, effectiveMeta, context);
|
|
127
|
+
entries.push(...children);
|
|
128
|
+
}
|
|
129
|
+
else if (entry.isFile()) {
|
|
130
|
+
entries.push({
|
|
131
|
+
sourcePath: fullPath,
|
|
132
|
+
relativePath: relative(baseDir, fullPath),
|
|
133
|
+
meta: effectiveMeta,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return entries;
|
|
138
|
+
};
|
|
139
|
+
const deduplicateByPath = (entries) => {
|
|
140
|
+
const byPath = new Map();
|
|
141
|
+
for (const entry of entries) {
|
|
142
|
+
const existing = byPath.get(entry.relativePath);
|
|
143
|
+
if (!existing) {
|
|
144
|
+
byPath.set(entry.relativePath, entry);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
// Same relative path collision: prefer entries from a type: "file" directory.
|
|
148
|
+
const existingIsFileType = existing.meta?.type === "file";
|
|
149
|
+
const currentIsFileType = entry.meta?.type === "file";
|
|
150
|
+
if (currentIsFileType && !existingIsFileType) {
|
|
151
|
+
byPath.set(entry.relativePath, entry);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return [...byPath.values()];
|
|
155
|
+
};
|
|
156
|
+
export const walkAssetRepo = async (repoDir, targetSemver, context) => {
|
|
157
|
+
const versionDirs = await findMatchingVersionDirs(repoDir, targetSemver);
|
|
158
|
+
const allEntries = [];
|
|
159
|
+
for (const versionDir of versionDirs) {
|
|
160
|
+
const meta = await readMetaJson(versionDir);
|
|
161
|
+
const entries = await walkDirectory(versionDir, versionDir, meta, context);
|
|
162
|
+
allEntries.push(...entries);
|
|
163
|
+
}
|
|
164
|
+
return deduplicateByPath(allEntries);
|
|
165
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export declare const attachCommand: import("citty").CommandDef<{
|
|
2
|
+
"juggling-project": {
|
|
3
|
+
type: "string";
|
|
4
|
+
description: string;
|
|
5
|
+
};
|
|
6
|
+
"accelplatform-version": {
|
|
7
|
+
type: "string";
|
|
8
|
+
description: string;
|
|
9
|
+
};
|
|
10
|
+
module: {
|
|
11
|
+
type: "string";
|
|
12
|
+
description: string;
|
|
13
|
+
};
|
|
14
|
+
name: {
|
|
15
|
+
type: "string";
|
|
16
|
+
description: string;
|
|
17
|
+
};
|
|
18
|
+
group: {
|
|
19
|
+
type: "string";
|
|
20
|
+
description: string;
|
|
21
|
+
};
|
|
22
|
+
"project-version": {
|
|
23
|
+
type: "string";
|
|
24
|
+
description: string;
|
|
25
|
+
};
|
|
26
|
+
description: {
|
|
27
|
+
type: "string";
|
|
28
|
+
description: string;
|
|
29
|
+
};
|
|
30
|
+
database: {
|
|
31
|
+
type: "string";
|
|
32
|
+
description: string;
|
|
33
|
+
};
|
|
34
|
+
javascript: {
|
|
35
|
+
type: "boolean";
|
|
36
|
+
description: string;
|
|
37
|
+
};
|
|
38
|
+
agent: {
|
|
39
|
+
type: "string";
|
|
40
|
+
description: string;
|
|
41
|
+
};
|
|
42
|
+
locale: {
|
|
43
|
+
type: "string";
|
|
44
|
+
description: string;
|
|
45
|
+
};
|
|
46
|
+
"skip-install": {
|
|
47
|
+
type: "boolean";
|
|
48
|
+
description: string;
|
|
49
|
+
default: false;
|
|
50
|
+
};
|
|
51
|
+
"non-interactive": {
|
|
52
|
+
type: "boolean";
|
|
53
|
+
description: string;
|
|
54
|
+
default: false;
|
|
55
|
+
};
|
|
56
|
+
"asset-source": {
|
|
57
|
+
type: "string";
|
|
58
|
+
description: string;
|
|
59
|
+
};
|
|
60
|
+
"asset-server-url": {
|
|
61
|
+
type: "string";
|
|
62
|
+
description: string;
|
|
63
|
+
};
|
|
64
|
+
}>;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import * as p from "@clack/prompts";
|
|
3
|
+
import { stat } from "node:fs/promises";
|
|
4
|
+
import { basename, join } from "node:path";
|
|
5
|
+
import { CLI_VERSION } from "../core/constants.js";
|
|
6
|
+
import { runPrompts } from "../interactive/prompts.js";
|
|
7
|
+
import { deployAssets } from "../asset/deployer.js";
|
|
8
|
+
import { createLocalAssetProvider } from "../asset/local-provider.js";
|
|
9
|
+
import { createFileAssetProvider } from "../asset/file-provider.js";
|
|
10
|
+
import { defaultAssetSourcePath } from "../asset/default-source.js";
|
|
11
|
+
import { getMessage } from "../i18n/index.js";
|
|
12
|
+
import { detectLocale } from "../utils/locale-detect.js";
|
|
13
|
+
export const attachCommand = defineCommand({
|
|
14
|
+
meta: {
|
|
15
|
+
name: "attach",
|
|
16
|
+
description: "Attach Accel CLI to an existing project",
|
|
17
|
+
},
|
|
18
|
+
args: {
|
|
19
|
+
"juggling-project": {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "Path to IM-Juggling project",
|
|
22
|
+
},
|
|
23
|
+
"accelplatform-version": {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "iAP version (e.g., 2026-Spring)",
|
|
26
|
+
},
|
|
27
|
+
module: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "Modules to use (comma-separated)",
|
|
30
|
+
},
|
|
31
|
+
name: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "Project name",
|
|
34
|
+
},
|
|
35
|
+
group: {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "Group name",
|
|
38
|
+
},
|
|
39
|
+
"project-version": {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "Project version",
|
|
42
|
+
},
|
|
43
|
+
description: {
|
|
44
|
+
type: "string",
|
|
45
|
+
description: "Project description",
|
|
46
|
+
},
|
|
47
|
+
database: {
|
|
48
|
+
type: "string",
|
|
49
|
+
description: "Database type",
|
|
50
|
+
},
|
|
51
|
+
javascript: {
|
|
52
|
+
type: "boolean",
|
|
53
|
+
description: "Use JavaScript",
|
|
54
|
+
},
|
|
55
|
+
agent: {
|
|
56
|
+
type: "string",
|
|
57
|
+
description: "Agent type",
|
|
58
|
+
},
|
|
59
|
+
locale: {
|
|
60
|
+
type: "string",
|
|
61
|
+
description: "Locale (ja, en, zh_CN)",
|
|
62
|
+
},
|
|
63
|
+
"skip-install": {
|
|
64
|
+
type: "boolean",
|
|
65
|
+
description: "Skip dependency installation",
|
|
66
|
+
default: false,
|
|
67
|
+
},
|
|
68
|
+
"non-interactive": {
|
|
69
|
+
type: "boolean",
|
|
70
|
+
description: "Non-interactive mode",
|
|
71
|
+
default: false,
|
|
72
|
+
},
|
|
73
|
+
"asset-source": {
|
|
74
|
+
type: "string",
|
|
75
|
+
description: "Local asset source path (tar archive or extracted directory). Defaults to the bundled assets/assets.tar.gz.",
|
|
76
|
+
},
|
|
77
|
+
"asset-server-url": {
|
|
78
|
+
type: "string",
|
|
79
|
+
description: "Asset server URL (takes precedence over --asset-source when explicitly set)",
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
run: async ({ args }) => {
|
|
83
|
+
const locale = args.locale ?? detectLocale();
|
|
84
|
+
const projectDir = process.cwd();
|
|
85
|
+
// Check .accel doesn't exist
|
|
86
|
+
try {
|
|
87
|
+
await stat(join(projectDir, ".accel"));
|
|
88
|
+
p.log.error(getMessage("error.accelExists", locale));
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// .accel doesn't exist — good
|
|
93
|
+
}
|
|
94
|
+
const moduleArg = args.module;
|
|
95
|
+
const modules = moduleArg
|
|
96
|
+
? moduleArg.split(",").map((m) => m.trim())
|
|
97
|
+
: undefined;
|
|
98
|
+
const agentArg = args.agent;
|
|
99
|
+
const promptOpts = {
|
|
100
|
+
name: args.name ?? basename(projectDir),
|
|
101
|
+
jugglingProject: args["juggling-project"],
|
|
102
|
+
accelplatformVersion: args["accelplatform-version"],
|
|
103
|
+
module: modules,
|
|
104
|
+
group: args.group,
|
|
105
|
+
projectVersion: args["project-version"],
|
|
106
|
+
description: args.description,
|
|
107
|
+
database: args.database,
|
|
108
|
+
javascript: args.javascript,
|
|
109
|
+
agent: agentArg
|
|
110
|
+
? agentArg.split(",").map((a) => a.trim())
|
|
111
|
+
: undefined,
|
|
112
|
+
locale,
|
|
113
|
+
noInteraction: args["non-interactive"],
|
|
114
|
+
isInit: false,
|
|
115
|
+
};
|
|
116
|
+
let resolved;
|
|
117
|
+
try {
|
|
118
|
+
resolved = await runPrompts(promptOpts);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
p.log.error(err instanceof Error ? err.message : String(err));
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
const settings = {
|
|
125
|
+
cliVersion: CLI_VERSION,
|
|
126
|
+
createdAt: new Date().toISOString(),
|
|
127
|
+
name: resolved.name,
|
|
128
|
+
group: resolved.group,
|
|
129
|
+
projectVersion: resolved.projectVersion,
|
|
130
|
+
description: resolved.description,
|
|
131
|
+
accelplatformVersion: resolved.accelplatformVersion,
|
|
132
|
+
modules: resolved.modules,
|
|
133
|
+
database: resolved.database,
|
|
134
|
+
agents: resolved.agents,
|
|
135
|
+
javascript: resolved.javascript,
|
|
136
|
+
locale: resolved.locale,
|
|
137
|
+
jugglingProject: resolved.jugglingProject,
|
|
138
|
+
deployedAssets: {},
|
|
139
|
+
};
|
|
140
|
+
const serverUrl = args["asset-server-url"];
|
|
141
|
+
const assetSource = args["asset-source"] ?? defaultAssetSourcePath();
|
|
142
|
+
const provider = serverUrl
|
|
143
|
+
? createLocalAssetProvider(serverUrl)
|
|
144
|
+
: createFileAssetProvider(assetSource);
|
|
145
|
+
p.log.info(getMessage("progress.deploying", locale));
|
|
146
|
+
const result = await deployAssets({
|
|
147
|
+
projectDir,
|
|
148
|
+
settings,
|
|
149
|
+
provider,
|
|
150
|
+
noInstall: args["skip-install"],
|
|
151
|
+
skipExistingFiles: true,
|
|
152
|
+
});
|
|
153
|
+
for (const path of result.skippedFiles) {
|
|
154
|
+
p.log.warn(getMessage("warning.fileExists", locale, { path }));
|
|
155
|
+
}
|
|
156
|
+
if (result.skippedFiles.length > 0) {
|
|
157
|
+
p.log.info(getMessage("attach.skipSummary", locale, {
|
|
158
|
+
count: String(result.skippedFiles.length),
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
p.log.success(getMessage("progress.complete", locale));
|
|
162
|
+
},
|
|
163
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const detachCommand: import("citty").CommandDef<{}>;
|