@namewta/speculo 1.0.6 → 1.0.7

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 (51) hide show
  1. package/dist/src/ops-resources.js +1 -1
  2. package/dist/src/ops-resources.js.map +1 -1
  3. package/package.json +1 -1
  4. package/template/workflows/ops/D-project-deploy/D-project-deploy.md +1 -1
  5. package/template/workflows/ops/H-host-manage/H-host-manage.md +1 -1
  6. package/template/workflows/ops/I-initialize/I-initialize.md +2 -2
  7. package/template/workflows/ops/README.md +2 -2
  8. package/template/workflows/ops/common/CAPABILITIES.md +2 -2
  9. package/template/workflows/ops/common/USAGE.md +24 -24
  10. package/template/workflows/ops/common/examples/README.md +1 -1
  11. package/template/workflows/ops/common/examples/register.example.json +1 -1
  12. package/template/workflows/ops/common/schemas/host.schema.json +1 -1
  13. package/template/workflows/ops/common/schemas/plan.schema.json +3 -3
  14. package/template/workflows/ops/common/schemas/spec.schema.json +1 -1
  15. package/template/workflows/ops/common/schemas/status.schema.json +1 -1
  16. package/template/workflows/ops/common/tests/test_ops.mjs +752 -0
  17. package/template/workflows/ops/common/tools/bootstrap.ps1 +2 -2
  18. package/template/workflows/ops/common/tools/bootstrap.sh +2 -2
  19. package/template/workflows/ops/common/tools/demo-local.mjs +101 -0
  20. package/template/workflows/ops/common/tools/ops.mjs +4 -0
  21. package/template/workflows/ops/common/tools/opslib/agent.mjs +873 -0
  22. package/template/workflows/ops/common/tools/opslib/cli.mjs +311 -0
  23. package/template/workflows/ops/common/tools/opslib/core.mjs +393 -0
  24. package/template/workflows/ops/common/tools/opslib/docs.mjs +252 -0
  25. package/template/workflows/ops/common/tools/opslib/execution.mjs +378 -0
  26. package/template/workflows/ops/common/tools/opslib/host_recipes.mjs +109 -0
  27. package/template/workflows/ops/common/tools/opslib/model.mjs +272 -0
  28. package/template/workflows/ops/common/tools/opslib/{native_windows.py → native_windows.mjs} +16 -12
  29. package/template/workflows/ops/common/tools/opslib/planner.mjs +687 -0
  30. package/template/workflows/ops/common/tools/opslib/services.mjs +76 -0
  31. package/template/workflows/ops/common/tools/opslib/sources.mjs +56 -0
  32. package/template/workflows/ops/common/tools/opslib/transport.mjs +127 -0
  33. package/template/workflows/ops/common/tools/validate-ops.mjs +43 -30
  34. package/template/workflows/ops/common/tests/test_ops.py +0 -392
  35. package/template/workflows/ops/common/tools/demo-local.py +0 -64
  36. package/template/workflows/ops/common/tools/ops.py +0 -7
  37. package/template/workflows/ops/common/tools/opslib/__init__.py +0 -2
  38. package/template/workflows/ops/common/tools/opslib/__pycache__/__init__.cpython-312.pyc +0 -0
  39. package/template/workflows/ops/common/tools/opslib/__pycache__/core.cpython-312.pyc +0 -0
  40. package/template/workflows/ops/common/tools/opslib/__pycache__/model.cpython-312.pyc +0 -0
  41. package/template/workflows/ops/common/tools/opslib/agent.py +0 -510
  42. package/template/workflows/ops/common/tools/opslib/cli.py +0 -172
  43. package/template/workflows/ops/common/tools/opslib/core.py +0 -199
  44. package/template/workflows/ops/common/tools/opslib/docs.py +0 -199
  45. package/template/workflows/ops/common/tools/opslib/execution.py +0 -248
  46. package/template/workflows/ops/common/tools/opslib/host_recipes.py +0 -67
  47. package/template/workflows/ops/common/tools/opslib/model.py +0 -199
  48. package/template/workflows/ops/common/tools/opslib/planner.py +0 -497
  49. package/template/workflows/ops/common/tools/opslib/services.py +0 -47
  50. package/template/workflows/ops/common/tools/opslib/sources.py +0 -27
  51. package/template/workflows/ops/common/tools/opslib/transport.py +0 -55
@@ -1,7 +1,7 @@
1
1
  param([switch]$Probe,[string]$Apply,[string]$Sha256,[string]$Approval)
2
2
  $ErrorActionPreference='Stop'
3
3
  if (-not $Apply) {
4
- Write-Output 'OPS bootstrap inventory (read-only; no Python prerequisite)'
4
+ Write-Output 'OPS bootstrap inventory (read-only; no extra runtime prerequisite beyond this probe)'
5
5
  Get-CimInstance Win32_OperatingSystem | Select-Object Caption,Version,OSArchitecture,FreePhysicalMemory,TotalVisibleMemorySize
6
6
  'ssh','git','python','py','uv','node','npm','java','docker','volta' | ForEach-Object {
7
7
  $c=Get-Command $_ -ErrorAction SilentlyContinue
@@ -15,4 +15,4 @@ if ($f.Attributes -band [IO.FileAttributes]::ReparsePoint) { throw 'Reparse-poin
15
15
  if ((Get-FileHash -LiteralPath $Apply -Algorithm SHA256).Hash.ToLowerInvariant() -ne $Sha256.ToLowerInvariant()) { throw 'Installer changed since review' }
16
16
  & $f.FullName
17
17
  if (-not $?) { throw 'Bootstrap installer failed' }
18
- if (-not (Get-Command python -ErrorAction SilentlyContinue) -and -not (Get-Command py -ErrorAction SilentlyContinue)) { throw 'Python still unavailable; not completed' }
18
+ if (-not (Get-Command node -ErrorAction SilentlyContinue)) { throw 'Node still unavailable; not completed' }
@@ -5,7 +5,7 @@ case "${1:---probe}" in
5
5
  --probe)
6
6
  printf 'OPS bootstrap inventory (read-only)\n'
7
7
  uname -srm
8
- for tool in ssh git python3 python uv node npm java docker volta curl wget sha256sum shasum; do
8
+ for tool in ssh git node npm uv python3 python java docker volta curl wget sha256sum shasum; do
9
9
  if command -v "$tool" >/dev/null 2>&1; then printf '%s=%s\n' "$tool" "$(command -v "$tool")"; else printf '%s=missing\n' "$tool"; fi
10
10
  done
11
11
  printf 'No software installed. Review a version-pinned OS/package-manager installer before --apply.\n'
@@ -21,7 +21,7 @@ case "${1:---probe}" in
21
21
  [ "$actual" = "$3" ] || { echo 'Installer changed after review' >&2; exit 2; }
22
22
  printf 'Executing explicitly approved bootstrap sha256=%s\n' "$actual"
23
23
  /bin/sh "$2"
24
- command -v python3 >/dev/null 2>&1 || command -v python >/dev/null 2>&1 || { echo 'Python still missing; not completed' >&2; exit 2; }
24
+ command -v node >/dev/null 2>&1 || { echo 'Node still missing; not completed' >&2; exit 2; }
25
25
  ;;
26
26
  *) echo 'usage: bootstrap.sh --probe | --apply FILE SHA256 I-APPROVE-THIS-BOOTSTRAP' >&2; exit 2;;
27
27
  esac
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ /** Real disposable local deployment. No network, system installation, or existing-root writes. */
3
+ import { existsSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
4
+ import { join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { initialize } from "./opslib/cli.mjs";
7
+ import { writeJson, OpsError, readJson } from "./opslib/core.mjs";
8
+ import { register, putCredential } from "./opslib/model.mjs";
9
+ import { probeLocal } from "./opslib/transport.mjs";
10
+ import { compilePlan } from "./opslib/planner.mjs";
11
+ import { approval, apply } from "./opslib/execution.mjs";
12
+
13
+ export function run(output) {
14
+ output = resolve(output);
15
+ if (existsSync(output) && readdirSync(output).length) {
16
+ throw new OpsError("demo output must be a new/empty directory, never your real state or target root");
17
+ }
18
+ mkdirSync(output, { recursive: true });
19
+ const state = join(output, "controller");
20
+ const target = join(output, "server");
21
+ initialize(state, "demo-controller");
22
+ const inventory = probeLocal();
23
+ const platform = inventory.platform;
24
+ if (!["linux", "darwin", "windows"].includes(platform)) throw new OpsError("unsupported platform");
25
+ register(state, {
26
+ hosts: [{
27
+ host_id: "demo-local", display_name: "Disposable local demo", platform, transport: "local",
28
+ connection: {}, root: target, identity: inventory.identity,
29
+ }],
30
+ projects: [{
31
+ project_id: "app-a", display_name: "Demo APP A", kind: "app", service_type: null,
32
+ source: { type: "local", location: join(output, "fixture-source"), revision: "demo-v1" },
33
+ }],
34
+ });
35
+ const password = "DEMO-ONLY-$literal-quote\"-not-a-real-password";
36
+ putCredential(state, {
37
+ credential_id: "demo-auth", version: 1,
38
+ purpose: "Disposable example only; never use in production",
39
+ values: { username: "demo-admin", password },
40
+ });
41
+ const program = `import { mkdirSync, writeFileSync } from "node:fs";
42
+ import { join } from "node:path";
43
+ const root = join(process.env.OPS_DATA_ROOT, "app", "storage");
44
+ mkdirSync(root, { recursive: true });
45
+ if (process.env.APP_USERNAME !== "demo-admin") throw new Error("username");
46
+ if (!process.env.APP_PASSWORD.startsWith("DEMO-ONLY-")) throw new Error("password");
47
+ writeFileSync(join(root, "record.json"), JSON.stringify({ version: "demo-v1", count: 1, environment_injected: true }));
48
+ `;
49
+ const spec = {
50
+ schema_version: 1, worker: "D", operation: "deploy",
51
+ reason: "Explicit disposable local demo; only this new output tree is written. No network or system install.",
52
+ rollback_note: "All files belong to the demo output. Keep evidence; no user data or shared service is touched.",
53
+ deployments: [{
54
+ deployment_id: "app-a-demo", project_id: "app-a", host_id: "demo-local", environment: "demo", instance: "main",
55
+ layout: "flat", method: "native", version: "demo-v1",
56
+ files: [{ path: "artifact/main.mjs", content: program }],
57
+ native: { supervisor: "oneshot", argv: [process.execPath, "{{artifact}}/main.mjs"] },
58
+ env: { "app.env": { APP_USERNAME: "{{credential:demo-auth@1:username}}", APP_PASSWORD: "{{credential:demo-auth@1:password}}" } },
59
+ credential_refs: ["demo-auth@1"],
60
+ health: [{ type: "file", path: "data/app/storage/record.json" }],
61
+ backup: "The finite process exits before backup. Demo has no shared dependencies. Actual backup must be a separately approved action.",
62
+ recovery: "Keep data/app/storage. Rerunning a different version requires a new approved plan.",
63
+ }],
64
+ };
65
+ writeJson(join(output, "demo-spec.json"), spec);
66
+ const plan = compilePlan(state, join(output, "demo-spec.json"));
67
+ approval(state, plan.run_id, plan.plan_digest, "demo-user", "I authorize only this fresh disposable demo directory and the exact generated fixture plan.");
68
+ const result = apply(state, plan.run_id);
69
+ if (result.status !== "completed") throw new OpsError("demo did not fully complete: " + JSON.stringify(result));
70
+ const local = join(state, "hosts", "demo-local", "deployments", "app-a-demo");
71
+ const readme = readFileSync(join(target, "app-a", "README.md"), "utf8");
72
+ if (readme.includes(password) || !readme.includes("demo-v1") || !readme.includes(join(target, "app-a", "data", "app", "storage"))) {
73
+ throw new OpsError("demo README checks failed");
74
+ }
75
+ if (!readFileSync(join(local, "README.md"), "utf8").includes(password)) throw new OpsError("controller README missing password");
76
+ if (!readFileSync(join(target, "app-a", "OPERATIONS.md"), "utf8").includes(password)) throw new OpsError("OPERATIONS.md missing password");
77
+ if (readJson(join(local, "docs-receipt.json")).status !== "both-sides-verified") throw new OpsError("docs receipt incomplete");
78
+ if (!readJson(join(target, "app-a", "data", "app", "storage", "record.json")).environment_injected) throw new OpsError("runtime data missing");
79
+ const report = {
80
+ ...result, output, target_readme: join(target, "app-a", "README.md"), controller_record: join(local, "README.md"),
81
+ checks: {
82
+ runtime_data_in_project: true, native_env_injection: true, server_readme_no_password: true,
83
+ controller_plaintext_exact: true, server_operations_plaintext_exact: true, both_sides_verified: true,
84
+ },
85
+ scope: "real local filesystem and subprocess fixture; not a Docker/SSH/Windows-service production acceptance test",
86
+ };
87
+ writeJson(join(output, "DEMO-RESULT.json"), report);
88
+ return report;
89
+ }
90
+
91
+ if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
92
+ const args = process.argv.slice(2);
93
+ const i = args.indexOf("--output");
94
+ try {
95
+ if (i < 0 || !args[i + 1]) throw new OpsError("--output is required");
96
+ process.stdout.write(JSON.stringify(run(args[i + 1]), null, 2) + "\n");
97
+ } catch (exc) {
98
+ process.stderr.write(String(exc.message || exc) + "\n");
99
+ process.exit(2);
100
+ }
101
+ }
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ /** OPS entry point: Node >=22, no third-party packages. */
3
+ import { main } from "./opslib/cli.mjs";
4
+ process.exit(main(process.argv.slice(2)));