aiakiv 0.2.1 → 0.3.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/dist/main.js CHANGED
@@ -6,10 +6,14 @@ import { runBind } from "./bind.js";
6
6
  import { ConsoleUsageError, runConsole, runConsoleKey } from "./console.js";
7
7
  import { runDashboard } from "./dashboard.js";
8
8
  import { DEFAULT_REPORT, DOCTOR_CLIENT_IDS, runDoctor } from "./doctor.js";
9
+ import { DEFAULT_PRECHECK_CONFIG, DEFAULT_PRECHECK_OUT, runEnterprisePrecheck } from "./enterprise/precheck.js";
10
+ import { realHost } from "./local/checks.js";
11
+ import { DEFAULT_ENGINE_PORT, runLocalDoctor } from "./local/doctor.js";
12
+ import { CONTAINER_EXPORT_DIR, CONTAINER_IMPORT_DIR, runLocalExport, runLocalImport } from "./local/portable.js";
9
13
  import { runSetup } from "./setup.js";
10
14
  import { DEFAULT_SKILLS_TARBALL_URL } from "./skills.js";
11
15
  import { DEFAULT_CONTROL_URL, DEFAULT_MCP_URL } from "./urls.js";
12
- import { VERSION } from "./version.js";
16
+ import { ENTERPRISE_ENGINE_VERSION, VERSION } from "./version.js";
13
17
  export const HELP = `aiakiv ${VERSION}: 폴더에 AiAkiv 프로젝트를 고정하고 확인하고, 전역 설치와 콘솔 작업을 하는 명령
14
18
 
15
19
  사용법:
@@ -21,6 +25,12 @@ export const HELP = `aiakiv ${VERSION}: 폴더에 AiAkiv 프로젝트를 고정
21
25
  npx aiakiv console describe [--json] [--dir <폴더>]
22
26
  npx aiakiv console key [--expires-days N] [--dir <폴더>] [--no-browser]
23
27
  npx aiakiv dashboard [--json] [--dir <폴더>]
28
+ npx aiakiv local doctor [--json] [--out <파일.md>] [--port <n>] [--offline]
29
+ npx aiakiv local export <팀> --to <파일|폴더> [--domain <이름>] [--yes] [--container <이름>]
30
+ npx aiakiv local import <파일|폴더> --team <팀> [--as <사용자>] [--domain <이름>] [--dry-run] [--resume] [--yes]
31
+ [--container <이름>]
32
+ npx aiakiv enterprise precheck [--config <toml>] [--image <참조> | --image-tar <파일>] [--device] [--no-browser]
33
+ [--no-login] [--only <영역,...>] [--skip <id 접두어,...>] [--redact-claims] [--out <폴더>] [--json]
24
34
 
25
35
  bind 폴더에 프로젝트를 고정합니다. 웹 콘솔의 "폴더 ZIP 받기" 와 같은 한 벌을 그 폴더에 직접 씁니다.
26
36
  claude, cursor 는 ?project= 주소(OAuth)를 쓰고, codex, gemini 는 브라우저에서 확인 버튼을 눌러
@@ -51,9 +61,48 @@ console key
51
61
  읽거나 쓸 수 없고 관리 작업만 합니다. 기본 만료 90일. 프로젝트나 팀을 고르지 않고, 속한
52
62
  모든 팀을 다룹니다(.gitignore 에 /.aiakiv/ 를 더합니다). 다시 받으면 앞선 계정 키는 폐기됩니다.
53
63
  dashboard
54
- 팀, 프로젝트, 저장 대상, 켜진 권한, 연결, 카드, 최근 저장 대상 이력을 한 화면에 보입니다.
64
+ 팀, 프로젝트, 저장 대상, 켜진 권한, 연결, 카드, 최근 저장 대상 이력, 사용량(오늘 저장 수와
65
+ 한도)을 한 화면에 보입니다.
55
66
  한 칸이 실패해도 나머지는 그립니다. 계정 키로는 저장 대상을 읽지 않고, 그 폴더에 프로젝트
56
67
  키가 있으면 그 키로 확인합니다.
68
+ local doctor
69
+ 자체 설치판(무료 5인 엔진)을 깔기 전에 이 컴퓨터가 준비됐는지 점검합니다. Node, OS, Docker(판,
70
+ compose v2, 리눅스 컨테이너 모드, 아키텍처, 메모리, CPU), 디스크 여유 공간, 포트(기본 ${DEFAULT_ENGINE_PORT}),
71
+ 인터넷 연결(Docker Hub, www.aiakiv.com, npm 저장소), 프록시 설정, 이미 깔린 엔진을 봅니다. 그 포트에
72
+ 엔진이 떠 있으면 엔진 상태, 판 이름, 판, 포트가 묶인 주소도 봅니다. 엔진 컨테이너가 떠 있으면 벡터가
73
+ 없어 검색에 나오지 않는 기억이 있는지 엔진에 묻고, 끊긴 가져오기가 있으면 이어서 할 명령을 알려 줍니다.
74
+ --json 은 보고서 JSON 하나를 내고,
75
+ --out 은 같은 내용을 마크다운 파일로 씁니다. --port 는 볼 포트를 바꾸고, --offline 이면 인터넷 연결은
76
+ 보지 않습니다. 종료 코드: 0 실패 없음, 3 실패 있음, 2 인자 오류, 4 Docker 를 찾지 못했거나 Docker 에
77
+ 닿지 못함(이때도 보고서는 냅니다).
78
+ local export, local import
79
+ 자체 설치판 엔진의 팀 기억을 .mweft.tar.gz 파일로 내보내고 받습니다. 본체는 엔진 컨테이너 안의
80
+ aiakiv-admin export, import 이고, 이 명령은 떠 있는 엔진 컨테이너를 찾아(compose 서비스 engine, 이미지
81
+ aiakiv/engine) docker exec 로 부르고, 파일을 docker cp 로 옮깁니다. --to 와 <파일> 은 이 컴퓨터의 경로이고,
82
+ 컨테이너 안에는 이 명령이 ${CONTAINER_EXPORT_DIR}, ${CONTAINER_IMPORT_DIR} 아래에 임시 폴더를 만들었다가 끝나면 지웁니다.
83
+ export 의 --to 가 폴더면 그 안에 둡니다. 팀의 도메인이 여럿이면 묶음(아카이브 여럿과 org_manifest.json)이라
84
+ --to 는 폴더가 됩니다. 이미 있는 파일은 --yes 가 있어야 덮어씁니다.
85
+ import 는 묻지 않습니다. --yes 가 없으면 엔진이 계획만 내고 종료 코드 2 로 끝납니다. --dry-run 은 계획만 내고 0,
86
+ --resume 은 끊긴 재임베딩을 이어서 합니다(같은 파일을 다시 주세요). --as 는 가져온 기억의 작성자(기본 팀 소유자).
87
+ 출력은 언제나 JSON 입니다(--json 은 붙여도 같습니다). export 는 한 문서, import 는 엔진의 진행 줄
88
+ ({"step","done","total"})을 받는 대로 내고 마지막 줄이 엔진의 결과입니다. 엔진의 실패 JSON 도 그대로 냅니다.
89
+ 벡터는 엔진이 다룹니다(이 명령은 파일 내용을 읽거나 바꾸지 않습니다). 엔진이 여럿 떠 있으면 --container 로 고릅니다.
90
+ 종료 코드: 엔진의 것을 그대로(0 성공, 2 입력 오류나 계획만, 3 거절, 4 엔진 DB 연결 실패, 5 가져오기 검증 실패).
91
+ 이 명령의 것은 2 인자 오류나 파일 없음이나 이미 있는 파일, 4 Docker 를 쓸 수 없음이나 엔진 컨테이너가 떠 있지
92
+ 않음, 1 docker cp 나 docker exec 실패, 엔진이 결과 없이 끝남.
93
+ enterprise precheck
94
+ 유료 기업판을 깔기 전에 회사의 신원 서버(OIDC), 데이터베이스, 파일 저장소, 메일 서버에 실제로 연결해 봅니다.
95
+ 점검은 유료 기업판 엔진 이미지 안의 aiakiv-admin precheck 가 하고, 이 명령은 Docker 로 그것을 돌립니다.
96
+ 설정 파일(기본 ./${DEFAULT_PRECHECK_CONFIG})이 있는 폴더가 컨테이너의 /config 로 붙고, 그 폴더의 .env 가
97
+ 있으면 함께 넘깁니다(비밀 값은 설정에서 env:이름 으로 가리킵니다. 이 명령은 .env 를 읽지 않습니다).
98
+ 이미지는 --image(이 컴퓨터에 있는 이미지), --image-tar(내려받은 tar, 같은 폴더의 <파일>.sha256 과 지문 비교),
99
+ 둘 다 없으면 aiakiv/engine-enterprise:${ENTERPRISE_ENGINE_VERSION} 이 있으면 그것, 없으면 www.aiakiv.com 에서
100
+ 내려받습니다(~/.aiakiv/cache). 로그인 단계에서 로그인 주소를 보이고 브라우저를 엽니다(--device 는 다른 기기에서
101
+ 넣을 주소와 코드, --no-browser 는 열지 않음). --no-login, --only, --skip, --redact-claims 는 점검에 그대로
102
+ 넘깁니다. 보고서는 그 폴더의 --out(기본 ${DEFAULT_PRECHECK_OUT}) 아래 report.json 과 report.md 입니다.
103
+ --json 이면 이 명령의 줄(cli.docker, cli.image) 뒤에 점검의 줄을 바꾸지 않고 내며 마지막 줄이 보고서입니다.
104
+ 종료 코드: 0 실패 없음, 3 실패 있음, 2 인자나 설정 오류, 4 Docker 를 쓸 수 없음, 5 이미지를 구하지 못함,
105
+ 1 점검이 보고서 없이 끝남.
57
106
 
58
107
  console 과 dashboard 가 키를 찾는 순서: 환경 변수 AIAKIV_API_KEY, <폴더>/.aiakiv/console.json(계정 키),
59
108
  <폴더>의 .codex/config.toml 과 .gemini/settings.json(프로젝트 키). 키 평문은 화면에 내지 않고 키 id 와
@@ -79,10 +128,13 @@ console 과 dashboard 의 종료 코드:
79
128
  AIAKIV_CONTROL_URL 웹 콘솔 주소(기본 ${DEFAULT_CONTROL_URL})
80
129
  AIAKIV_API_KEY console, dashboard 가 먼저 쓰는 키
81
130
  AIAKIV_SKILLS_TARBALL_URL 폴더 스킬과 전역 스킬을 받는 주소(기본 ${DEFAULT_SKILLS_TARBALL_URL})
131
+ AIAKIV_ENTERPRISE_MANIFEST_URL enterprise precheck 가 이미지를 내려받을 때 읽는 목록 파일 주소
82
132
 
83
133
  공통: -h, --help -v, --version`;
84
134
  class UsageError extends Error {
85
135
  }
136
+ /** local 의 하위 명령 안내(모르는 하위 명령, 하위 명령 없음). */
137
+ const LOCAL_SUBS = "doctor, export, import 셋입니다";
86
138
  function parseExpiresDays(v) {
87
139
  if (v === undefined)
88
140
  return undefined;
@@ -91,6 +143,15 @@ function parseExpiresDays(v) {
91
143
  throw new UsageError("--expires-days 는 1 이상의 정수입니다.");
92
144
  return n;
93
145
  }
146
+ function parsePort(v) {
147
+ if (v === undefined)
148
+ return undefined;
149
+ const n = Number(v);
150
+ if (!/^\d+$/.test(v.trim()) || !Number.isInteger(n) || n < 1 || n > 65535) {
151
+ throw new UsageError(`--port 는 1 에서 65535 사이의 정수입니다(받은 값: ${v}).`);
152
+ }
153
+ return n;
154
+ }
94
155
  function parseClient(v, required) {
95
156
  if (v === undefined) {
96
157
  if (required)
@@ -281,6 +342,170 @@ export async function main(argv, ctx) {
281
342
  action: positionals[0], data: values.data, json: values.json, dir: values.dir, yes: values.yes,
282
343
  });
283
344
  }
345
+ if (cmd === "local") {
346
+ // 자체 설치판(무료 5인 엔진) 명령. 지금은 doctor, export, import(docs/211 §5.1, §7, §17). install, status 등이
347
+ // 같은 아래에 붙는다.
348
+ const [sub, ...subRest] = rest;
349
+ if (sub === "-h" || sub === "--help" || sub === "help")
350
+ return (ctx.out(HELP), 0);
351
+ if (!sub)
352
+ throw new UsageError(`local 뒤에 하위 명령이 필요합니다(지금은 ${LOCAL_SUBS}).`);
353
+ if (sub === "export") {
354
+ const { values, positionals } = parseArgs({
355
+ args: subRest,
356
+ options: {
357
+ to: { type: "string" },
358
+ domain: { type: "string" },
359
+ yes: { type: "boolean", default: false },
360
+ container: { type: "string" },
361
+ json: { type: "boolean", default: false },
362
+ help: { type: "boolean", short: "h" },
363
+ },
364
+ strict: true,
365
+ allowPositionals: true,
366
+ });
367
+ if (values.help)
368
+ return (ctx.out(HELP), 0);
369
+ if (positionals.length !== 1) {
370
+ throw new UsageError(positionals.length
371
+ ? `local export 는 팀 이름 하나를 받습니다(받은 값: ${positionals.join(" ")}).`
372
+ : "local export 에 팀 이름이 필요합니다. 예: npx aiakiv local export 연구팀 --to ./연구팀.mweft.tar.gz");
373
+ }
374
+ if (values.to === undefined)
375
+ throw new UsageError("--to <파일 또는 폴더> 가 필요합니다(호스트 경로).");
376
+ for (const name of ["to", "domain", "container"]) {
377
+ const v = values[name];
378
+ if (v !== undefined && !v.trim())
379
+ throw new UsageError(`--${name} 에 값이 필요합니다.`);
380
+ }
381
+ if (!positionals[0].trim())
382
+ throw new UsageError("팀 이름이 비어 있습니다.");
383
+ return await runLocalExport(ctx, {
384
+ team: positionals[0], to: values.to, domain: values.domain, yes: values.yes, container: values.container,
385
+ });
386
+ }
387
+ if (sub === "import") {
388
+ const { values, positionals } = parseArgs({
389
+ args: subRest,
390
+ options: {
391
+ team: { type: "string" },
392
+ as: { type: "string" },
393
+ domain: { type: "string" },
394
+ "dry-run": { type: "boolean", default: false },
395
+ resume: { type: "boolean", default: false },
396
+ yes: { type: "boolean", default: false },
397
+ container: { type: "string" },
398
+ json: { type: "boolean", default: false },
399
+ help: { type: "boolean", short: "h" },
400
+ },
401
+ strict: true,
402
+ allowPositionals: true,
403
+ });
404
+ if (values.help)
405
+ return (ctx.out(HELP), 0);
406
+ if (positionals.length !== 1) {
407
+ throw new UsageError(positionals.length
408
+ ? `local import 는 파일 하나를 받습니다(받은 값: ${positionals.join(" ")}).`
409
+ : "local import 에 가져올 파일이 필요합니다. 예: npx aiakiv local import ./연구팀.mweft.tar.gz --team 연구팀");
410
+ }
411
+ if (values.team === undefined)
412
+ throw new UsageError("--team <팀> 이 필요합니다.");
413
+ for (const name of ["team", "as", "domain", "container"]) {
414
+ const v = values[name];
415
+ if (v !== undefined && !v.trim())
416
+ throw new UsageError(`--${name} 에 값이 필요합니다.`);
417
+ }
418
+ if (!positionals[0].trim())
419
+ throw new UsageError("가져올 파일 경로가 비어 있습니다.");
420
+ return await runLocalImport(ctx, {
421
+ file: positionals[0],
422
+ team: values.team,
423
+ as: values.as,
424
+ domain: values.domain,
425
+ dryRun: values["dry-run"],
426
+ resume: values.resume,
427
+ yes: values.yes,
428
+ container: values.container,
429
+ });
430
+ }
431
+ if (sub !== "doctor")
432
+ throw new UsageError(`local 의 모르는 하위 명령입니다: ${sub}. 지금은 ${LOCAL_SUBS}.`);
433
+ const { values, positionals } = parseArgs({
434
+ args: subRest,
435
+ options: {
436
+ json: { type: "boolean", default: false },
437
+ out: { type: "string" },
438
+ port: { type: "string" },
439
+ offline: { type: "boolean", default: false },
440
+ help: { type: "boolean", short: "h" },
441
+ },
442
+ strict: true,
443
+ allowPositionals: true,
444
+ });
445
+ if (values.help)
446
+ return (ctx.out(HELP), 0);
447
+ if (positionals.length)
448
+ throw new UsageError(`local doctor 는 위치 인자를 받지 않습니다: ${positionals.join(" ")}`);
449
+ if (values.out !== undefined && !values.out.trim())
450
+ throw new UsageError("--out 에 파일 이름이 필요합니다.");
451
+ return await runLocalDoctor(ctx, { json: values.json, out: values.out, port: parsePort(values.port), offline: values.offline }, realHost());
452
+ }
453
+ if (cmd === "enterprise") {
454
+ // 유료 기업판 명령. 지금은 precheck 하나(K2G docs/212 BP-173 §3, P3). 점검 본체는 엔진 이미지 안에 있다.
455
+ const [sub, ...subRest] = rest;
456
+ if (sub === "-h" || sub === "--help" || sub === "help")
457
+ return (ctx.out(HELP), 0);
458
+ if (!sub)
459
+ throw new UsageError("enterprise 뒤에 하위 명령이 필요합니다(지금은 precheck 하나입니다).");
460
+ if (sub !== "precheck") {
461
+ throw new UsageError(`enterprise 의 모르는 하위 명령입니다: ${sub}. 지금은 precheck 하나입니다.`);
462
+ }
463
+ const { values, positionals } = parseArgs({
464
+ args: subRest,
465
+ options: {
466
+ config: { type: "string" },
467
+ image: { type: "string" },
468
+ "image-tar": { type: "string" },
469
+ device: { type: "boolean", default: false },
470
+ "no-browser": { type: "boolean", default: false },
471
+ "no-login": { type: "boolean", default: false },
472
+ only: { type: "string" },
473
+ skip: { type: "string" },
474
+ "redact-claims": { type: "boolean", default: false },
475
+ out: { type: "string" },
476
+ json: { type: "boolean", default: false },
477
+ help: { type: "boolean", short: "h" },
478
+ },
479
+ strict: true,
480
+ allowPositionals: true,
481
+ });
482
+ if (values.help)
483
+ return (ctx.out(HELP), 0);
484
+ if (positionals.length) {
485
+ throw new UsageError(`enterprise precheck 는 위치 인자를 받지 않습니다: ${positionals.join(" ")}`);
486
+ }
487
+ if (values.image !== undefined && values["image-tar"] !== undefined) {
488
+ throw new UsageError("--image 와 --image-tar 는 함께 줄 수 없습니다. 둘 중 하나만 주세요.");
489
+ }
490
+ for (const name of ["config", "image", "image-tar", "only", "skip", "out"]) {
491
+ const v = values[name];
492
+ if (v !== undefined && !v.trim())
493
+ throw new UsageError(`--${name} 에 값이 필요합니다.`);
494
+ }
495
+ return await runEnterprisePrecheck(ctx, {
496
+ config: values.config,
497
+ image: values.image,
498
+ imageTar: values["image-tar"],
499
+ device: values.device,
500
+ noBrowser: values["no-browser"],
501
+ noLogin: values["no-login"],
502
+ only: values.only,
503
+ skip: values.skip,
504
+ redactClaims: values["redact-claims"],
505
+ out: values.out,
506
+ json: values.json,
507
+ });
508
+ }
284
509
  if (cmd === "dashboard") {
285
510
  const { values } = parseArgs({
286
511
  args: rest,
package/dist/proc.js CHANGED
@@ -93,6 +93,57 @@ export const run = (file, args, opts = {}) => new Promise((resolve) => {
93
93
  });
94
94
  });
95
95
  });
96
+ /** 조각으로 들어오는 글자를 줄로 자른다. 마지막 줄이 줄바꿈 없이 끝나도 flush 가 넘긴다. */
97
+ function lineSplitter(emit) {
98
+ let buf = "";
99
+ const send = (l) => emit(l.replace(/\r$/, "").replace(ANSI, ""));
100
+ return {
101
+ push: (d) => {
102
+ buf += d;
103
+ let i;
104
+ while ((i = buf.indexOf("\n")) >= 0) {
105
+ send(buf.slice(0, i));
106
+ buf = buf.slice(i + 1);
107
+ }
108
+ },
109
+ flush: () => {
110
+ if (buf)
111
+ send(buf);
112
+ buf = "";
113
+ },
114
+ };
115
+ }
116
+ export const runLines = (file, args, opts) => new Promise((resolve) => {
117
+ const viaCmd = process.platform === "win32" && /\.(cmd|bat)$/i.test(file);
118
+ const env = { ...process.env, NO_COLOR: "1", FORCE_COLOR: "0" };
119
+ const stdio = ["ignore", "pipe", "pipe"];
120
+ let child;
121
+ try {
122
+ child = viaCmd
123
+ ? spawn([quoteForCmd(file), ...args.map(quoteForCmd)].join(" "), {
124
+ shell: true,
125
+ stdio,
126
+ env,
127
+ cwd: opts.cwd,
128
+ windowsHide: true,
129
+ })
130
+ : spawn(file, args, { stdio, env, cwd: opts.cwd, windowsHide: true });
131
+ }
132
+ catch (e) {
133
+ resolve({ code: null, error: e.message });
134
+ return;
135
+ }
136
+ const out = lineSplitter(opts.onStdout);
137
+ const err = lineSplitter(opts.onStderr);
138
+ child.stdout?.setEncoding("utf8").on("data", (d) => out.push(d));
139
+ child.stderr?.setEncoding("utf8").on("data", (d) => err.push(d));
140
+ child.on("error", (e) => resolve({ code: null, error: e.message }));
141
+ child.on("close", (code) => {
142
+ out.flush();
143
+ err.flush();
144
+ resolve({ code });
145
+ });
146
+ });
96
147
  /** 실행 결과를 사람이 읽을 한 줄 이유로. */
97
148
  export function failReason(r) {
98
149
  if (r.error)
package/dist/snippet.js CHANGED
@@ -27,17 +27,13 @@ export function mcpUrl(base, project) {
27
27
  const url = `${base}/mcp`;
28
28
  return project === undefined ? url : `${url}?project=${encodeURIComponent(project)}`;
29
29
  }
30
- /** 프로젝트를 지정하지 않는 OAuth 연결(전역 설치)의 주소 `{base}/mcp?apps=all`.
31
- * 표시 없는 `{base}/mcp` 에 OAuth 로 붙고 `?project=` 도 없으면 서버가 앱 실행 도구
32
- * (`run_aiakiv_app_action`)를 뺀 표면만 준다. `?apps=all` 이 전체 도구를 고른다.
33
- * `?project=` 주소(mcpUrl 의 project)와 함께 쓰지 않는다 — 그 연결은 이미 전체를 받는다. */
34
- export function mcpUrlAllApps(base) {
35
- return `${mcpUrl(base)}?apps=all`;
36
- }
37
30
  /** Cursor 원클릭 설치 딥링크 — 전역 설치(머리글 없음, Main 을 따라간다).
38
- * config = base64(JSON { url }). 주소는 mcpUrlAllApps. */
31
+ * config = base64(JSON { url }). 주소는 표시 없는 `{base}/mcp`(BP-171 §7.1, 공개 주소에서
32
+ * 앱 전체 표시를 뺐다). 이 연결은 기억 도구와 카드 도구를 받고 앱 실행 도구
33
+ * (`run_aiakiv_app_action`)는 받지 않는다. 관리는 `npx aiakiv console`, 대화 안 앱 기능은
34
+ * 폴더 연결(`?project=`)이나 API 키 연결로 한다. */
39
35
  export function cursorInstallLink(base) {
40
- const config = btoa(JSON.stringify({ url: mcpUrlAllApps(base) }));
36
+ const config = btoa(JSON.stringify({ url: mcpUrl(base) }));
41
37
  return `cursor://anysphere.cursor-deeplink/mcp/install?name=AiAkiv&config=${config}`;
42
38
  }
43
39
  /** 내 프로젝트의 연결 주소와 머리글 — 복사 스니펫과 ZIP 이 같이 쓴다.
package/dist/version.js CHANGED
@@ -1,2 +1,6 @@
1
1
  // package.json 의 version 과 같아야 한다(tests/cli.test.mjs 가 확인).
2
- export const VERSION = "0.2.1";
2
+ export const VERSION = "0.3.0";
3
+ // 이 CLI 판이 `aiakiv enterprise precheck` 에서 쓰는 유료 기업판 엔진 이미지 판(aiakiv/engine-enterprise:<판>).
4
+ // K2G packaging/selfhost/build.sh 가 `<이미지>:<판>` 으로 태그를 붙이고(앞에 v 없음), 엔진 판은 K2G
5
+ // pyproject.toml 의 version(이미지의 AIAKIV_ENGINE_VERSION)이다. 엔진 판을 올리면 이 값도 같이 올린다.
6
+ export const ENTERPRISE_ENGINE_VERSION = "0.1.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiakiv",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "AiAkiv CLI: pin a folder to an AiAkiv project (connection, folder skills, settings), check it, and run console actions and a dashboard from the shell.",
5
5
  "license": "MIT",
6
6
  "type": "module",