@semiont/cli 0.3.0 → 0.3.1

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.
Files changed (2) hide show
  1. package/dist/cli.mjs +412 -70
  2. package/package.json +7 -7
package/dist/cli.mjs CHANGED
@@ -512,8 +512,8 @@ var init_parseUtil = __esm({
512
512
  init_errors();
513
513
  init_en();
514
514
  makeIssue = (params) => {
515
- const { data, path: path45, errorMaps, issueData } = params;
516
- const fullPath = [...path45, ...issueData.path || []];
515
+ const { data, path: path46, errorMaps, issueData } = params;
516
+ const fullPath = [...path46, ...issueData.path || []];
517
517
  const fullIssue = {
518
518
  ...issueData,
519
519
  path: fullPath
@@ -821,11 +821,11 @@ var init_types = __esm({
821
821
  init_parseUtil();
822
822
  init_util();
823
823
  ParseInputLazyPath = class {
824
- constructor(parent, value, path45, key) {
824
+ constructor(parent, value, path46, key) {
825
825
  this._cachedPath = [];
826
826
  this.parent = parent;
827
827
  this.data = value;
828
- this._path = path45;
828
+ this._path = path46;
829
829
  this._key = key;
830
830
  }
831
831
  get path() {
@@ -5626,8 +5626,8 @@ var init_filesystem_service = __esm({
5626
5626
  async checkHealth() {
5627
5627
  const dataPath = this.getDataPath();
5628
5628
  try {
5629
- const fs53 = await import("fs");
5630
- await fs53.promises.access(dataPath, fs53.constants.R_OK | fs53.constants.W_OK);
5629
+ const fs54 = await import("fs");
5630
+ await fs54.promises.access(dataPath, fs54.constants.R_OK | fs54.constants.W_OK);
5631
5631
  return {
5632
5632
  healthy: true,
5633
5633
  details: {
@@ -7157,11 +7157,11 @@ var init_backend_check = __esm({
7157
7157
  let logs;
7158
7158
  if (fs11.existsSync(appLogPath)) {
7159
7159
  try {
7160
- const { execFileSync: execFileSync44 } = __require("child_process");
7161
- const recentLogs = execFileSync44("tail", ["-10", appLogPath], { encoding: "utf-8" }).split("\n").filter((line) => line.trim());
7160
+ const { execFileSync: execFileSync45 } = __require("child_process");
7161
+ const recentLogs = execFileSync45("tail", ["-10", appLogPath], { encoding: "utf-8" }).split("\n").filter((line) => line.trim());
7162
7162
  let errorLogs = [];
7163
7163
  if (fs11.existsSync(errorLogPath)) {
7164
- errorLogs = execFileSync44("tail", ["-5", errorLogPath], { encoding: "utf-8" }).split("\n").filter((line) => line.trim());
7164
+ errorLogs = execFileSync45("tail", ["-5", errorLogPath], { encoding: "utf-8" }).split("\n").filter((line) => line.trim());
7165
7165
  }
7166
7166
  logs = {
7167
7167
  recent: recentLogs,
@@ -7377,11 +7377,11 @@ var init_frontend_check = __esm({
7377
7377
  let logs;
7378
7378
  if (fs12.existsSync(appLogPath)) {
7379
7379
  try {
7380
- const { execFileSync: execFileSync44 } = __require("child_process");
7381
- const recentLogs = execFileSync44("tail", ["-10", appLogPath], { encoding: "utf-8" }).split("\n").filter((line) => line.trim());
7380
+ const { execFileSync: execFileSync45 } = __require("child_process");
7381
+ const recentLogs = execFileSync45("tail", ["-10", appLogPath], { encoding: "utf-8" }).split("\n").filter((line) => line.trim());
7382
7382
  let errorLogs = [];
7383
7383
  if (fs12.existsSync(errorLogPath)) {
7384
- errorLogs = execFileSync44("tail", ["-5", errorLogPath], { encoding: "utf-8" }).split("\n").filter((line) => line.trim());
7384
+ errorLogs = execFileSync45("tail", ["-5", errorLogPath], { encoding: "utf-8" }).split("\n").filter((line) => line.trim());
7385
7385
  }
7386
7386
  logs = {
7387
7387
  recent: recentLogs,
@@ -8029,8 +8029,8 @@ async function startJanusGraph(context) {
8029
8029
  for (let i = 0; i < maxAttempts; i++) {
8030
8030
  await new Promise((resolve9) => setTimeout(resolve9, 2e3));
8031
8031
  try {
8032
- const { execFileSync: execFileSync44 } = await import("child_process");
8033
- execFileSync44(gremlinShellScript, ["-e", "g.V().count()"], {
8032
+ const { execFileSync: execFileSync45 } = await import("child_process");
8033
+ execFileSync45(gremlinShellScript, ["-e", "g.V().count()"], {
8034
8034
  stdio: "ignore",
8035
8035
  timeout: 5e3
8036
8036
  });
@@ -8081,9 +8081,9 @@ async function startJanusGraph(context) {
8081
8081
  }
8082
8082
  };
8083
8083
  }
8084
- async function fileExists(path45) {
8084
+ async function fileExists(path46) {
8085
8085
  try {
8086
- await fs17.access(path45);
8086
+ await fs17.access(path46);
8087
8087
  return true;
8088
8088
  } catch {
8089
8089
  return false;
@@ -8911,8 +8911,8 @@ var init_filesystem_provision = __esm({
8911
8911
  metadata.directories.push(dirPath);
8912
8912
  }
8913
8913
  try {
8914
- const { execFileSync: execFileSync44 } = __require("child_process");
8915
- const dfOutput = execFileSync44("df", ["-h", absolutePath], { encoding: "utf-8" });
8914
+ const { execFileSync: execFileSync45 } = __require("child_process");
8915
+ const dfOutput = execFileSync45("df", ["-h", absolutePath], { encoding: "utf-8" });
8916
8916
  const lines = dfOutput.split("\n");
8917
8917
  if (lines.length > 1) {
8918
8918
  const stats = lines[1].split(/\s+/);
@@ -9017,9 +9017,9 @@ Files placed here will persist across service restarts.
9017
9017
  // src/platforms/posix/handlers/graph-provision.ts
9018
9018
  import * as fs23 from "fs/promises";
9019
9019
  import { execFileSync as execFileSync9 } from "child_process";
9020
- async function fileExists2(path45) {
9020
+ async function fileExists2(path46) {
9021
9021
  try {
9022
- await fs23.access(path45);
9022
+ await fs23.access(path46);
9023
9023
  return true;
9024
9024
  } catch {
9025
9025
  return false;
@@ -10268,9 +10268,9 @@ async function stopJanusGraph(context) {
10268
10268
  };
10269
10269
  }
10270
10270
  }
10271
- async function fileExists3(path45) {
10271
+ async function fileExists3(path46) {
10272
10272
  try {
10273
- await fs27.access(path45);
10273
+ await fs27.access(path46);
10274
10274
  return true;
10275
10275
  } catch {
10276
10276
  return false;
@@ -12801,9 +12801,9 @@ async function startJanusGraph2(context) {
12801
12801
  };
12802
12802
  }
12803
12803
  }
12804
- async function fileExists4(path45) {
12804
+ async function fileExists4(path46) {
12805
12805
  try {
12806
- await fs36.access(path45);
12806
+ await fs36.access(path46);
12807
12807
  return true;
12808
12808
  } catch {
12809
12809
  return false;
@@ -15956,9 +15956,9 @@ async function stopJanusGraph2(context) {
15956
15956
  };
15957
15957
  }
15958
15958
  }
15959
- async function fileExists5(path45) {
15959
+ async function fileExists5(path46) {
15960
15960
  try {
15961
- await fs39.access(path45);
15961
+ await fs39.access(path46);
15962
15962
  return true;
15963
15963
  } catch {
15964
15964
  return false;
@@ -22006,12 +22006,12 @@ var init_path = __esm({
22006
22006
  "node_modules/@anthropic-ai/sdk/internal/utils/path.mjs"() {
22007
22007
  init_error();
22008
22008
  EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
22009
- createPathTagFunction = (pathEncoder = encodeURIPath) => function path45(statics, ...params) {
22009
+ createPathTagFunction = (pathEncoder = encodeURIPath) => function path46(statics, ...params) {
22010
22010
  if (statics.length === 1)
22011
22011
  return statics[0];
22012
22012
  let postPath = false;
22013
22013
  const invalidSegments = [];
22014
- const path46 = statics.reduce((previousValue, currentValue, index) => {
22014
+ const path47 = statics.reduce((previousValue, currentValue, index) => {
22015
22015
  if (/[?#]/.test(currentValue)) {
22016
22016
  postPath = true;
22017
22017
  }
@@ -22028,7 +22028,7 @@ var init_path = __esm({
22028
22028
  }
22029
22029
  return previousValue + currentValue + (index === params.length ? "" : encoded);
22030
22030
  }, "");
22031
- const pathOnly = path46.split(/[?#]/, 1)[0];
22031
+ const pathOnly = path47.split(/[?#]/, 1)[0];
22032
22032
  const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi;
22033
22033
  let match;
22034
22034
  while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) {
@@ -22049,10 +22049,10 @@ var init_path = __esm({
22049
22049
  }, "");
22050
22050
  throw new AnthropicError(`Path parameters result in path with invalid segments:
22051
22051
  ${invalidSegments.map((e) => e.error).join("\n")}
22052
- ${path46}
22052
+ ${path47}
22053
22053
  ${underline}`);
22054
22054
  }
22055
- return path46;
22055
+ return path47;
22056
22056
  };
22057
22057
  path34 = /* @__PURE__ */ createPathTagFunction(encodeURIPath);
22058
22058
  }
@@ -24735,9 +24735,9 @@ var init_client = __esm({
24735
24735
  makeStatusError(status, error, message, headers) {
24736
24736
  return APIError.generate(status, error, message, headers);
24737
24737
  }
24738
- buildURL(path45, query, defaultBaseURL) {
24738
+ buildURL(path46, query, defaultBaseURL) {
24739
24739
  const baseURL = !__classPrivateFieldGet(this, _BaseAnthropic_instances, "m", _BaseAnthropic_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
24740
- const url = isAbsoluteURL(path45) ? new URL(path45) : new URL(baseURL + (baseURL.endsWith("/") && path45.startsWith("/") ? path45.slice(1) : path45));
24740
+ const url = isAbsoluteURL(path46) ? new URL(path46) : new URL(baseURL + (baseURL.endsWith("/") && path46.startsWith("/") ? path46.slice(1) : path46));
24741
24741
  const defaultQuery = this.defaultQuery();
24742
24742
  if (!isEmptyObj(defaultQuery)) {
24743
24743
  query = { ...defaultQuery, ...query };
@@ -24768,24 +24768,24 @@ var init_client = __esm({
24768
24768
  */
24769
24769
  async prepareRequest(request, { url, options }) {
24770
24770
  }
24771
- get(path45, opts) {
24772
- return this.methodRequest("get", path45, opts);
24771
+ get(path46, opts) {
24772
+ return this.methodRequest("get", path46, opts);
24773
24773
  }
24774
- post(path45, opts) {
24775
- return this.methodRequest("post", path45, opts);
24774
+ post(path46, opts) {
24775
+ return this.methodRequest("post", path46, opts);
24776
24776
  }
24777
- patch(path45, opts) {
24778
- return this.methodRequest("patch", path45, opts);
24777
+ patch(path46, opts) {
24778
+ return this.methodRequest("patch", path46, opts);
24779
24779
  }
24780
- put(path45, opts) {
24781
- return this.methodRequest("put", path45, opts);
24780
+ put(path46, opts) {
24781
+ return this.methodRequest("put", path46, opts);
24782
24782
  }
24783
- delete(path45, opts) {
24784
- return this.methodRequest("delete", path45, opts);
24783
+ delete(path46, opts) {
24784
+ return this.methodRequest("delete", path46, opts);
24785
24785
  }
24786
- methodRequest(method, path45, opts) {
24786
+ methodRequest(method, path46, opts) {
24787
24787
  return this.request(Promise.resolve(opts).then((opts2) => {
24788
- return { method, path: path45, ...opts2 };
24788
+ return { method, path: path46, ...opts2 };
24789
24789
  }));
24790
24790
  }
24791
24791
  request(options, remainingRetries = null) {
@@ -24889,8 +24889,8 @@ var init_client = __esm({
24889
24889
  }));
24890
24890
  return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
24891
24891
  }
24892
- getAPIList(path45, Page2, opts) {
24893
- return this.requestAPIList(Page2, { method: "get", path: path45, ...opts });
24892
+ getAPIList(path46, Page2, opts) {
24893
+ return this.requestAPIList(Page2, { method: "get", path: path46, ...opts });
24894
24894
  }
24895
24895
  requestAPIList(Page2, options) {
24896
24896
  const request = this.makeRequest(options, null, void 0);
@@ -24977,8 +24977,8 @@ var init_client = __esm({
24977
24977
  }
24978
24978
  async buildRequest(inputOptions, { retryCount = 0 } = {}) {
24979
24979
  const options = { ...inputOptions };
24980
- const { method, path: path45, query, defaultBaseURL } = options;
24981
- const url = this.buildURL(path45, query, defaultBaseURL);
24980
+ const { method, path: path46, query, defaultBaseURL } = options;
24981
+ const url = this.buildURL(path46, query, defaultBaseURL);
24982
24982
  if ("timeout" in options)
24983
24983
  validatePositiveInteger("timeout", options.timeout);
24984
24984
  options.timeout = options.timeout ?? this.timeout;
@@ -27405,7 +27405,7 @@ var require_package = __commonJS({
27405
27405
  "package.json"(exports, module) {
27406
27406
  module.exports = {
27407
27407
  name: "@semiont/cli",
27408
- version: "0.3.0",
27408
+ version: "0.3.1",
27409
27409
  description: "Semiont CLI - Unified environment management tool",
27410
27410
  _comment: "AWS SDK dependencies (@aws-sdk/*) are only used by platforms/aws",
27411
27411
  type: "module",
@@ -27469,12 +27469,12 @@ var require_package = __commonJS({
27469
27469
  "@aws-sdk/client-secrets-manager": "^3.600.0",
27470
27470
  "@aws-sdk/client-sts": "^3.859.0",
27471
27471
  "@aws-sdk/client-wafv2": "^3.859.0",
27472
- "@semiont/api-client": "^0.3.0",
27473
- "@semiont/content": "^0.3.0",
27474
- "@semiont/core": "^0.3.0",
27475
- "@semiont/event-sourcing": "^0.3.0",
27476
- "@semiont/graph": "^0.3.0",
27477
- "@semiont/make-meaning": "^0.3.0",
27472
+ "@semiont/api-client": "^0.3.1",
27473
+ "@semiont/content": "^0.3.1",
27474
+ "@semiont/core": "^0.3.1",
27475
+ "@semiont/event-sourcing": "^0.3.1",
27476
+ "@semiont/graph": "^0.3.1",
27477
+ "@semiont/make-meaning": "^0.3.1",
27478
27478
  arg: "^5.0.2",
27479
27479
  argon2: "^0.43.0",
27480
27480
  express: "^4.18.2",
@@ -29433,6 +29433,347 @@ var importCmd = new CommandBuilder().name("import").description("Import resource
29433
29433
  }
29434
29434
  }).schema(ImportOptionsSchema).handler(runImport).build();
29435
29435
 
29436
+ // src/core/commands/local.ts
29437
+ init_zod();
29438
+ init_cli_colors();
29439
+ init_command_definition();
29440
+ init_base_options_schema();
29441
+ import * as fs52 from "fs";
29442
+ import * as path42 from "path";
29443
+ import * as readline from "readline";
29444
+ import { execFileSync as execFileSync44 } from "child_process";
29445
+ var LocalOptionsSchema = BaseOptionsSchema.extend({
29446
+ email: external_exports.string().optional(),
29447
+ password: external_exports.string().optional(),
29448
+ generatePassword: external_exports.boolean().default(true)
29449
+ }).transform((data) => ({
29450
+ ...data,
29451
+ environment: data.environment || "_local_"
29452
+ }));
29453
+ function prompt(question) {
29454
+ return new Promise((resolve9) => {
29455
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
29456
+ rl.question(question, (answer) => {
29457
+ rl.close();
29458
+ resolve9(answer.trim());
29459
+ });
29460
+ });
29461
+ }
29462
+ function promptPassword(question) {
29463
+ return new Promise((resolve9) => {
29464
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
29465
+ process.stdout.write(question);
29466
+ let password = "";
29467
+ process.stdin.setRawMode?.(true);
29468
+ process.stdin.resume();
29469
+ process.stdin.setEncoding("utf8");
29470
+ const onData = (ch) => {
29471
+ if (ch === "\n" || ch === "\r" || ch === "") {
29472
+ process.stdin.setRawMode?.(false);
29473
+ process.stdin.pause();
29474
+ process.stdin.removeListener("data", onData);
29475
+ process.stdout.write("\n");
29476
+ rl.close();
29477
+ resolve9(password);
29478
+ } else if (ch === "\x7F") {
29479
+ password = password.slice(0, -1);
29480
+ } else {
29481
+ password += ch;
29482
+ process.stdout.write("*");
29483
+ }
29484
+ };
29485
+ process.stdin.on("data", onData);
29486
+ });
29487
+ }
29488
+ function runSemiont(args, env, captureOutput = false) {
29489
+ return execFileSync44("semiont", args, {
29490
+ env,
29491
+ stdio: captureOutput ? "pipe" : "inherit",
29492
+ encoding: "utf8"
29493
+ });
29494
+ }
29495
+ function runSemiontSafe(args, env) {
29496
+ try {
29497
+ const output = execFileSync44("semiont", args, {
29498
+ env,
29499
+ stdio: "pipe",
29500
+ encoding: "utf8"
29501
+ });
29502
+ return { success: true, output: output || "", error: "" };
29503
+ } catch (err) {
29504
+ return {
29505
+ success: false,
29506
+ output: err.stdout || "",
29507
+ error: err.stderr || err.message || ""
29508
+ };
29509
+ }
29510
+ }
29511
+ var REQUIRED_SERVICES = ["database", "filesystem", "backend", "frontend", "proxy"];
29512
+ var EXTERNAL_SERVICES = ["graph", "inference"];
29513
+ var ALL_SERVICES = [...REQUIRED_SERVICES, ...EXTERNAL_SERVICES];
29514
+ function parseCheckOutput(jsonOutput) {
29515
+ try {
29516
+ const parsed = JSON.parse(jsonOutput);
29517
+ const results = [];
29518
+ const items = parsed.results || [];
29519
+ for (const item of items) {
29520
+ const name = item.entity || "";
29521
+ const meta = item.metadata || {};
29522
+ const statusStr = meta.status || "unknown";
29523
+ const health = meta.health;
29524
+ const healthy = item.success && (health?.healthy ?? statusStr === "running");
29525
+ const status = statusStr === "running" && healthy ? "healthy" : statusStr === "running" ? "unhealthy" : statusStr === "stopped" ? "stopped" : "unknown";
29526
+ results.push({
29527
+ name,
29528
+ status,
29529
+ healthy,
29530
+ external: EXTERNAL_SERVICES.includes(name)
29531
+ });
29532
+ }
29533
+ return results;
29534
+ } catch {
29535
+ return [];
29536
+ }
29537
+ }
29538
+ function isProvisioned(serviceName, semiotRoot) {
29539
+ switch (serviceName) {
29540
+ case "backend":
29541
+ return fs52.existsSync(path42.join(semiotRoot, "backend", ".env"));
29542
+ case "frontend":
29543
+ return fs52.existsSync(path42.join(semiotRoot, "frontend", ".env"));
29544
+ case "filesystem":
29545
+ return fs52.existsSync(path42.join(semiotRoot, "data"));
29546
+ case "database":
29547
+ case "proxy":
29548
+ // For these, rely on check result only — we don't have a simple local sentinel
29549
+ default:
29550
+ return false;
29551
+ }
29552
+ }
29553
+ async function local(options) {
29554
+ const startTime = Date.now();
29555
+ const envVarsToAdvise = [];
29556
+ const warnings = [];
29557
+ const results = {
29558
+ command: "local",
29559
+ environment: "local",
29560
+ timestamp: /* @__PURE__ */ new Date(),
29561
+ duration: 0,
29562
+ results: [],
29563
+ summary: { total: 0, succeeded: 0, failed: 0, warnings: 0 },
29564
+ executionContext: {
29565
+ user: process.env.USER || "unknown",
29566
+ workingDirectory: process.cwd(),
29567
+ dryRun: false
29568
+ }
29569
+ };
29570
+ console.log(`
29571
+ ${colors.bright}\u{1F310} Semiont Local Setup${colors.reset}
29572
+ `);
29573
+ try {
29574
+ let semiotRoot = process.env.SEMIONT_ROOT || "";
29575
+ if (!semiotRoot) {
29576
+ const defaultPath = path42.join(process.env.HOME || process.cwd(), "semiont");
29577
+ const answer = await prompt(
29578
+ `${colors.cyan}SEMIONT_ROOT is not set.${colors.reset}
29579
+ Press Enter to use ${colors.bright}${defaultPath}${colors.reset}, or type a path: `
29580
+ );
29581
+ semiotRoot = answer || defaultPath;
29582
+ fs52.mkdirSync(semiotRoot, { recursive: true });
29583
+ process.env.SEMIONT_ROOT = semiotRoot;
29584
+ envVarsToAdvise.push(`export SEMIONT_ROOT=${semiotRoot}`);
29585
+ console.log(`${colors.green}\u2713${colors.reset} Using ${semiotRoot}
29586
+ `);
29587
+ } else {
29588
+ console.log(`${colors.green}\u2713${colors.reset} SEMIONT_ROOT=${semiotRoot}
29589
+ `);
29590
+ }
29591
+ let semiotEnv = process.env.SEMIONT_ENV || "";
29592
+ if (!semiotEnv) {
29593
+ semiotEnv = "local";
29594
+ process.env.SEMIONT_ENV = semiotEnv;
29595
+ envVarsToAdvise.push(`export SEMIONT_ENV=local`);
29596
+ console.log(`${colors.dim}SEMIONT_ENV not set, using "local"${colors.reset}
29597
+ `);
29598
+ } else {
29599
+ console.log(`${colors.green}\u2713${colors.reset} SEMIONT_ENV=${semiotEnv}
29600
+ `);
29601
+ }
29602
+ const env = { ...process.env };
29603
+ const semiontJsonPath = path42.join(semiotRoot, "semiont.json");
29604
+ const envFilePath = path42.join(semiotRoot, "environments", `${semiotEnv}.json`);
29605
+ const isInitialized = fs52.existsSync(semiontJsonPath) && fs52.existsSync(envFilePath);
29606
+ if (isInitialized) {
29607
+ console.log(`${colors.green}\u2713${colors.reset} Project already initialized
29608
+ `);
29609
+ } else {
29610
+ console.log(`${colors.cyan}\u25B6 Initializing project...${colors.reset}`);
29611
+ try {
29612
+ runSemiont(["init"], env);
29613
+ console.log(`${colors.green}\u2713${colors.reset} Project initialized
29614
+ `);
29615
+ } catch {
29616
+ console.error(`${colors.red}\u2717 semiont init failed \u2014 cannot continue${colors.reset}`);
29617
+ results.summary.failed = 1;
29618
+ results.duration = Date.now() - startTime;
29619
+ return results;
29620
+ }
29621
+ }
29622
+ console.log(`${colors.cyan}\u25B6 Checking services...${colors.reset}`);
29623
+ const checkResult = runSemiontSafe(["check", "--all", "--output", "json"], env);
29624
+ const serviceStatuses = parseCheckOutput(checkResult.output);
29625
+ const statusByName = new Map(
29626
+ serviceStatuses.map((s) => [s.name, s])
29627
+ );
29628
+ for (const serviceName of ALL_SERVICES) {
29629
+ const info = statusByName.get(serviceName);
29630
+ const isExternal = EXTERNAL_SERVICES.includes(serviceName);
29631
+ if (!info) {
29632
+ if (isExternal) {
29633
+ warnings.push(`${serviceName}: not found in check output (external \u2014 skipping)`);
29634
+ console.log(` ${colors.yellow}\u26A0 ${serviceName}${colors.reset} (external): not found in check output`);
29635
+ continue;
29636
+ }
29637
+ console.log(` ${colors.yellow}? ${serviceName}${colors.reset}: not found \u2014 provisioning...`);
29638
+ } else if (info.healthy) {
29639
+ console.log(` ${colors.green}\u2713 ${serviceName}${colors.reset}: running`);
29640
+ continue;
29641
+ } else if (isExternal && !info.healthy) {
29642
+ warnings.push(`${serviceName}: unhealthy (external service \u2014 check credentials)`);
29643
+ console.log(` ${colors.yellow}\u26A0 ${serviceName}${colors.reset} (external): unhealthy \u2014 check your credentials`);
29644
+ continue;
29645
+ }
29646
+ const alreadyProvisioned = info ? info.status === "stopped" && isProvisioned(serviceName, semiotRoot) : false;
29647
+ if (!alreadyProvisioned) {
29648
+ console.log(` ${colors.cyan} provisioning ${serviceName}...${colors.reset}`);
29649
+ const provResult = runSemiontSafe(["provision", "--service", serviceName], env);
29650
+ if (!provResult.success) {
29651
+ const combinedOutput = provResult.error + provResult.output;
29652
+ if (combinedOutput.includes("does not support capability 'provision'")) {
29653
+ } else {
29654
+ const msg = `Failed to provision ${serviceName}: ${provResult.error}`;
29655
+ if (isExternal) {
29656
+ warnings.push(msg);
29657
+ console.log(` ${colors.yellow} \u26A0 ${msg}${colors.reset}`);
29658
+ continue;
29659
+ } else {
29660
+ throw new Error(msg);
29661
+ }
29662
+ }
29663
+ }
29664
+ }
29665
+ console.log(` ${colors.cyan} starting ${serviceName}...${colors.reset}`);
29666
+ const startResult = runSemiontSafe(["start", "--service", serviceName], env);
29667
+ if (!startResult.success) {
29668
+ const combinedOutput = startResult.error + startResult.output;
29669
+ if (combinedOutput.includes("does not support capability 'start'")) {
29670
+ } else {
29671
+ const msg = `Failed to start ${serviceName}: ${startResult.error}`;
29672
+ if (isExternal) {
29673
+ warnings.push(msg);
29674
+ console.log(` ${colors.yellow} \u26A0 ${msg}${colors.reset}`);
29675
+ } else {
29676
+ throw new Error(msg);
29677
+ }
29678
+ }
29679
+ } else {
29680
+ console.log(` ${colors.green} \u2713 ${serviceName} started${colors.reset}`);
29681
+ }
29682
+ }
29683
+ console.log("");
29684
+ const credentialsPath = path42.join(semiotRoot, "credentials.txt");
29685
+ if (fs52.existsSync(credentialsPath)) {
29686
+ console.log(`${colors.green}\u2713${colors.reset} Credentials file already exists at ${credentialsPath}
29687
+ `);
29688
+ } else {
29689
+ console.log(`${colors.cyan}\u25B6 Creating admin user...${colors.reset}`);
29690
+ let email = options.email;
29691
+ if (!email) {
29692
+ const answer = await prompt(` Admin email [admin@local]: `);
29693
+ email = answer || "admin@local";
29694
+ }
29695
+ let generatePassword2 = options.generatePassword;
29696
+ if (!options.password) {
29697
+ const answer = await prompt(` Generate password? [Y/n]: `);
29698
+ generatePassword2 = answer === "" || answer.toLowerCase() === "y";
29699
+ }
29700
+ const useraddArgs = ["useradd", "--email", email, "--admin"];
29701
+ if (generatePassword2) {
29702
+ useraddArgs.push("--generate-password");
29703
+ } else {
29704
+ const pw = options.password || await promptPassword(` Password: `);
29705
+ useraddArgs.push("--password", pw);
29706
+ }
29707
+ const useraddResult = runSemiontSafe(useraddArgs, env);
29708
+ if (useraddResult.success) {
29709
+ const output = useraddResult.output;
29710
+ console.log(output);
29711
+ fs52.writeFileSync(credentialsPath, output, { mode: 384 });
29712
+ console.log(`${colors.green}\u2713${colors.reset} Credentials saved to ${credentialsPath}`);
29713
+ console.log(`${colors.yellow} \u26A0 credentials.txt contains a plaintext password \u2014 keep it safe${colors.reset}
29714
+ `);
29715
+ } else {
29716
+ console.log(`${colors.yellow}\u26A0 useradd failed: ${useraddResult.error}${colors.reset}`);
29717
+ console.log(` Run manually: semiont useradd --email <email> --generate-password --admin
29718
+ `);
29719
+ warnings.push(`useradd failed: ${useraddResult.error}`);
29720
+ }
29721
+ }
29722
+ console.log(`${colors.cyan}\u25B6 Final service check...${colors.reset}
29723
+ `);
29724
+ runSemiont(["check", "--all"], env);
29725
+ console.log(`
29726
+ ${colors.bright}${colors.green}\u2713 Semiont is running at http://localhost:8080${colors.reset}
29727
+ `);
29728
+ console.log(` Credentials: ${credentialsPath}`);
29729
+ if (warnings.length > 0) {
29730
+ console.log(`
29731
+ ${colors.yellow}Warnings:${colors.reset}`);
29732
+ for (const w of warnings) {
29733
+ console.log(` ${colors.yellow}\u26A0${colors.reset} ${w}`);
29734
+ }
29735
+ }
29736
+ if (envVarsToAdvise.length > 0) {
29737
+ console.log(`
29738
+ To persist your environment across sessions, add to your shell profile:`);
29739
+ for (const line of envVarsToAdvise) {
29740
+ console.log(` ${colors.cyan}${line}${colors.reset}`);
29741
+ }
29742
+ }
29743
+ results.summary.succeeded = 1;
29744
+ results.metadata = { semiotRoot, semiotEnv, credentialsPath };
29745
+ } catch (error) {
29746
+ const msg = error instanceof Error ? error.message : String(error);
29747
+ console.error(`
29748
+ ${colors.red}\u2717 Setup failed: ${msg}${colors.reset}`);
29749
+ results.summary.failed = 1;
29750
+ results.error = msg;
29751
+ }
29752
+ results.duration = Date.now() - startTime;
29753
+ return results;
29754
+ }
29755
+ var localCommand = new CommandBuilder().name("local").description("Set up and start Semiont locally (init + provision + start + useradd)").schema(LocalOptionsSchema).args(withBaseArgs({
29756
+ "--email": {
29757
+ type: "string",
29758
+ description: "Admin user email (default: admin@local)"
29759
+ },
29760
+ "--password": {
29761
+ type: "string",
29762
+ description: "Admin user password (default: auto-generate)"
29763
+ },
29764
+ "--generate-password": {
29765
+ type: "boolean",
29766
+ description: "Generate a random admin password",
29767
+ default: true
29768
+ }
29769
+ }, {
29770
+ "--email": "--email"
29771
+ })).requiresEnvironment(false).requiresServices(false).examples(
29772
+ "semiont local",
29773
+ "semiont local --email me@example.com",
29774
+ "semiont local --email me@example.com --generate-password"
29775
+ ).setupHandler(local).build();
29776
+
29436
29777
  // src/core/command-discovery.ts
29437
29778
  var commandCache = /* @__PURE__ */ new Map();
29438
29779
  var COMMANDS = {
@@ -29449,7 +29790,8 @@ var COMMANDS = {
29449
29790
  "restore": restoreCmd,
29450
29791
  "verify": verifyCmd,
29451
29792
  "export": exportCmd,
29452
- "import": importCmd
29793
+ "import": importCmd,
29794
+ "local": localCommand
29453
29795
  };
29454
29796
  async function loadCommand(name) {
29455
29797
  if (commandCache.has(name)) {
@@ -29495,8 +29837,8 @@ function isCommandDefinition(obj) {
29495
29837
 
29496
29838
  // src/core/service-discovery.ts
29497
29839
  init_config_loader();
29498
- import * as path42 from "path";
29499
- import * as fs52 from "fs";
29840
+ import * as path43 from "path";
29841
+ import * as fs53 from "fs";
29500
29842
  var BUILT_IN_SERVICES = ["frontend", "backend", "database", "filesystem"];
29501
29843
  var environmentServicesCache = /* @__PURE__ */ new Map();
29502
29844
  async function loadEnvironmentServices(environment) {
@@ -29505,11 +29847,11 @@ async function loadEnvironmentServices(environment) {
29505
29847
  }
29506
29848
  try {
29507
29849
  const PROJECT_ROOT = findProjectRoot();
29508
- const configPath = path42.join(PROJECT_ROOT, "environments", `${environment}.json`);
29509
- if (!fs52.existsSync(configPath)) {
29850
+ const configPath = path43.join(PROJECT_ROOT, "environments", `${environment}.json`);
29851
+ if (!fs53.existsSync(configPath)) {
29510
29852
  return [...BUILT_IN_SERVICES];
29511
29853
  }
29512
- const jsonContent = fs52.readFileSync(configPath, "utf-8");
29854
+ const jsonContent = fs53.readFileSync(configPath, "utf-8");
29513
29855
  const config = JSON.parse(jsonContent);
29514
29856
  const services = config.services ? Object.keys(config.services) : [];
29515
29857
  environmentServicesCache.set(environment, services);
@@ -29540,7 +29882,7 @@ async function isValidService(service, environment) {
29540
29882
  import { parseEnvironment as parseEnvironment2 } from "@semiont/core";
29541
29883
 
29542
29884
  // src/core/service-resolver.ts
29543
- import * as path43 from "path";
29885
+ import * as path44 from "path";
29544
29886
  import { ConfigurationError as ConfigurationError2 } from "@semiont/core";
29545
29887
  function getServicePlatform(serviceName, config) {
29546
29888
  const environment = config._metadata?.environment;
@@ -29588,7 +29930,7 @@ function resolveServiceDeployments(serviceNames, config) {
29588
29930
  const serviceConfig = config.services?.[serviceName];
29589
29931
  if (!serviceConfig) {
29590
29932
  const availableServices = Object.keys(config.services || {});
29591
- const configPath = path43.join(projectRoot, "environments", `${environment}.json`);
29933
+ const configPath = path44.join(projectRoot, "environments", `${environment}.json`);
29592
29934
  console.warn(`\u274C Service '${serviceName}' not found in environment '${environment}'`);
29593
29935
  if (availableServices.length > 0) {
29594
29936
  console.warn(` Available services: ${availableServices.join(", ")}`);
@@ -29627,7 +29969,7 @@ function resolveServiceDeployments(serviceNames, config) {
29627
29969
  // src/core/command-service-matcher.ts
29628
29970
  init_service_factory();
29629
29971
  init_service_command_capabilities();
29630
- import * as path44 from "path";
29972
+ import * as path45 from "path";
29631
29973
  async function checkServiceSupportsCommand(serviceName, command, envConfig) {
29632
29974
  try {
29633
29975
  const projectRoot = envConfig._metadata?.projectRoot;
@@ -29711,7 +30053,7 @@ async function resolveServiceSelector(selector, capability, envConfig) {
29711
30053
  if (!projectRoot) {
29712
30054
  throw new Error("Project root is required in envConfig._metadata");
29713
30055
  }
29714
- const configPath = path44.join(projectRoot, "environments", `${environment}.json`);
30056
+ const configPath = path45.join(projectRoot, "environments", `${environment}.json`);
29715
30057
  const errorMessage = [
29716
30058
  `Unknown service '${selector}' in environment '${environment}'`,
29717
30059
  `Available services: ${availableServices.join(", ")}`,
@@ -30207,14 +30549,14 @@ var OutputFormatter = class {
30207
30549
  /**
30208
30550
  * Get nested value from object using dot notation
30209
30551
  */
30210
- static getNestedValue(obj, path45) {
30211
- return path45.split(".").reduce((current, key) => current?.[key], obj);
30552
+ static getNestedValue(obj, path46) {
30553
+ return path46.split(".").reduce((current, key) => current?.[key], obj);
30212
30554
  }
30213
30555
  /**
30214
30556
  * Set nested value in object using dot notation
30215
30557
  */
30216
- static setNestedValue(obj, path45, value) {
30217
- const keys = path45.split(".");
30558
+ static setNestedValue(obj, path46, value) {
30559
+ const keys = path46.split(".");
30218
30560
  const lastKey = keys.pop();
30219
30561
  const target = keys.reduce((current, key) => {
30220
30562
  if (!(key in current)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@semiont/cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Semiont CLI - Unified environment management tool",
5
5
  "_comment": "AWS SDK dependencies (@aws-sdk/*) are only used by platforms/aws",
6
6
  "type": "module",
@@ -64,12 +64,12 @@
64
64
  "@aws-sdk/client-secrets-manager": "^3.600.0",
65
65
  "@aws-sdk/client-sts": "^3.859.0",
66
66
  "@aws-sdk/client-wafv2": "^3.859.0",
67
- "@semiont/api-client": "0.3.0",
68
- "@semiont/content": "0.3.0",
69
- "@semiont/core": "0.3.0",
70
- "@semiont/event-sourcing": "0.3.0",
71
- "@semiont/graph": "0.3.0",
72
- "@semiont/make-meaning": "0.3.0",
67
+ "@semiont/api-client": "0.3.1",
68
+ "@semiont/content": "0.3.1",
69
+ "@semiont/core": "0.3.1",
70
+ "@semiont/event-sourcing": "0.3.1",
71
+ "@semiont/graph": "0.3.1",
72
+ "@semiont/make-meaning": "0.3.1",
73
73
  "arg": "^5.0.2",
74
74
  "argon2": "^0.43.0",
75
75
  "express": "^4.18.2",