@roj-ai/standalone-server 0.1.11 → 0.1.13

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 (48) hide show
  1. package/dist/files-upload-route.d.ts +36 -0
  2. package/dist/files-upload-route.d.ts.map +1 -0
  3. package/dist/files-upload-route.js +50 -0
  4. package/dist/files-upload-route.js.map +1 -0
  5. package/dist/git-instance-fs.d.ts +60 -0
  6. package/dist/git-instance-fs.d.ts.map +1 -0
  7. package/dist/git-instance-fs.js +164 -0
  8. package/dist/git-instance-fs.js.map +1 -0
  9. package/dist/instance.d.ts +6 -0
  10. package/dist/instance.d.ts.map +1 -1
  11. package/dist/instance.js +5 -0
  12. package/dist/instance.js.map +1 -1
  13. package/dist/local-registry.d.ts +109 -0
  14. package/dist/local-registry.d.ts.map +1 -0
  15. package/dist/local-registry.js +252 -0
  16. package/dist/local-registry.js.map +1 -0
  17. package/dist/main.js +7 -1
  18. package/dist/main.js.map +1 -1
  19. package/dist/platform-api.d.ts +11 -1
  20. package/dist/platform-api.d.ts.map +1 -1
  21. package/dist/platform-api.js +176 -13
  22. package/dist/platform-api.js.map +1 -1
  23. package/dist/server.d.ts +7 -1
  24. package/dist/server.d.ts.map +1 -1
  25. package/dist/server.js +31 -1
  26. package/dist/server.js.map +1 -1
  27. package/dist/session-file-route.d.ts +24 -0
  28. package/dist/session-file-route.d.ts.map +1 -0
  29. package/dist/session-file-route.js +72 -0
  30. package/dist/session-file-route.js.map +1 -0
  31. package/dist/signed-token.d.ts +25 -0
  32. package/dist/signed-token.d.ts.map +1 -0
  33. package/dist/signed-token.js +50 -0
  34. package/dist/signed-token.js.map +1 -0
  35. package/dist/user-config-loader.d.ts.map +1 -1
  36. package/dist/user-config-loader.js +37 -1
  37. package/dist/user-config-loader.js.map +1 -1
  38. package/package.json +5 -5
  39. package/src/files-upload-route.ts +64 -0
  40. package/src/git-instance-fs.ts +187 -0
  41. package/src/instance.ts +12 -1
  42. package/src/local-registry.ts +334 -0
  43. package/src/main.ts +7 -1
  44. package/src/platform-api.ts +251 -13
  45. package/src/server.ts +47 -2
  46. package/src/session-file-route.ts +99 -0
  47. package/src/signed-token.ts +68 -0
  48. package/src/user-config-loader.ts +41 -2
@@ -0,0 +1,36 @@
1
+ /**
2
+ * POST /api/v1/files/upload — multipart endpoint matching the `@roj-ai/client/platform.files.upload`
3
+ * two-phase contract:
4
+ *
5
+ * 1. Preflight with `contentHash`, `filename`, `mimeType` only. If we already have
6
+ * the bytes (sha-256 match), respond `{ ok, fileId, ..., deduped: true }`.
7
+ * Otherwise respond 409 `{ error: 'file-required' }`.
8
+ * 2. Retry with the same fields plus a `file` blob. We persist the bytes,
9
+ * record the file, and respond `{ ok, fileId, ..., deduped: false }`.
10
+ *
11
+ * No bearer auth (standalone is open) — but we accept and ignore an
12
+ * `Authorization: Bearer …` header so client code copy-pasted from the platform
13
+ * keeps working.
14
+ */
15
+ import type { Context } from 'hono';
16
+ import type { LocalRegistry } from './local-registry.js';
17
+ interface Deps {
18
+ registry: LocalRegistry;
19
+ }
20
+ export declare function createFilesUploadRoute(deps: Deps): (c: Context) => Promise<(Response & import("hono").TypedResponse<{
21
+ ok: false;
22
+ error: string;
23
+ }, 400, "json">) | (Response & import("hono").TypedResponse<{
24
+ ok: false;
25
+ error: "file-required";
26
+ }, 409, "json">) | (Response & import("hono").TypedResponse<{
27
+ ok: true;
28
+ fileId: string;
29
+ filename: string;
30
+ mimeType: string;
31
+ size: number;
32
+ r2Key: string;
33
+ deduped: boolean;
34
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">)>;
35
+ export {};
36
+ //# sourceMappingURL=files-upload-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files-upload-route.d.ts","sourceRoot":"","sources":["../src/files-upload-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAExD,UAAU,IAAI;IACb,QAAQ,EAAE,aAAa,CAAA;CACvB;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,IAAI,IAClC,GAAG,OAAO;;;;;;;;;;;;;;oEAwCxB"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * POST /api/v1/files/upload — multipart endpoint matching the `@roj-ai/client/platform.files.upload`
3
+ * two-phase contract:
4
+ *
5
+ * 1. Preflight with `contentHash`, `filename`, `mimeType` only. If we already have
6
+ * the bytes (sha-256 match), respond `{ ok, fileId, ..., deduped: true }`.
7
+ * Otherwise respond 409 `{ error: 'file-required' }`.
8
+ * 2. Retry with the same fields plus a `file` blob. We persist the bytes,
9
+ * record the file, and respond `{ ok, fileId, ..., deduped: false }`.
10
+ *
11
+ * No bearer auth (standalone is open) — but we accept and ignore an
12
+ * `Authorization: Bearer …` header so client code copy-pasted from the platform
13
+ * keeps working.
14
+ */
15
+ export function createFilesUploadRoute(deps) {
16
+ return async (c) => {
17
+ let form;
18
+ try {
19
+ form = await c.req.formData();
20
+ }
21
+ catch {
22
+ return c.json({ ok: false, error: 'invalid_multipart' }, 400);
23
+ }
24
+ const contentHash = form.get('contentHash');
25
+ const filename = form.get('filename');
26
+ const mimeType = form.get('mimeType');
27
+ if (typeof contentHash !== 'string' || typeof filename !== 'string' || typeof mimeType !== 'string') {
28
+ return c.json({ ok: false, error: 'invalid_form' }, 400);
29
+ }
30
+ const file = form.get('file');
31
+ const buffer = file && typeof file !== 'string' && 'arrayBuffer' in file
32
+ ? Buffer.from(await file.arrayBuffer())
33
+ : undefined;
34
+ const result = await deps.registry.uploadFile({ buffer, filename, mimeType, contentHash });
35
+ if ('error' in result) {
36
+ // Preflight miss — client retries with body.
37
+ return c.json({ ok: false, error: result.error }, 409);
38
+ }
39
+ return c.json({
40
+ ok: true,
41
+ fileId: result.fileId,
42
+ filename: result.filename,
43
+ mimeType: result.mimeType,
44
+ size: result.size,
45
+ r2Key: '',
46
+ deduped: result.deduped,
47
+ });
48
+ };
49
+ }
50
+ //# sourceMappingURL=files-upload-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files-upload-route.js","sourceRoot":"","sources":["../src/files-upload-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AASH,MAAM,UAAU,sBAAsB,CAAC,IAAU;IAChD,OAAO,KAAK,EAAE,CAAU,EAAE,EAAE;QAC3B,IAAI,IAAc,CAAA;QAClB,IAAI,CAAC;YACJ,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAA;QAC9B,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,GAAG,CAAC,CAAA;QAC9D,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;QACrC,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACrG,OAAO,CAAC,CAAC,IAAI,CACZ,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,EACpC,GAAG,CACH,CAAA;QACF,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAC7B,MAAM,MAAM,GAAG,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,aAAa,IAAI,IAAI;YACvE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;YACvC,CAAC,CAAC,SAAS,CAAA;QAEZ,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAA;QAE1F,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;YACvB,6CAA6C;YAC7C,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,EAAE,GAAG,CAAC,CAAA;QACvD,CAAC;QAED,OAAO,CAAC,CAAC,IAAI,CAAC;YACb,EAAE,EAAE,IAAI;YACR,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,KAAK,EAAE,EAAE;YACT,OAAO,EAAE,MAAM,CAAC,OAAO;SACvB,CAAC,CAAA;IACH,CAAC,CAAA;AACF,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Per-instance git layout for the standalone server.
3
+ *
4
+ * Mirrors what `roj-platform` does inside an E2B sandbox: each instance owns
5
+ * a bare repo, each session owns a worktree branched from `main`. The SDK
6
+ * receives the worktree path as `workspaceDir` on `sessions.create` and treats
7
+ * it as an opaque directory — it doesn't know about git.
8
+ *
9
+ * {dataPath}/instances/{instanceId}/repo.git/ ← bare, branch `main`
10
+ * {dataPath}/instances/{instanceId}/sessions/{sid}/ ← worktree, branch `session/{sid}`
11
+ *
12
+ * Why a bare repo + worktrees instead of a plain dir per session:
13
+ * - Parallel sessions on one instance need independent commit history.
14
+ * - `git worktree` shares object storage, so the cost is mostly metadata.
15
+ * - Mental model matches platform — agent `git status` / `git log` behave
16
+ * identically against standalone and against E2B.
17
+ *
18
+ * Idempotent: `initInstance` skips if `repo.git` already exists,
19
+ * `addSessionWorktree` skips if the target worktree is already registered.
20
+ * That keeps `roj-standalone` restart-safe — bare repos and worktrees on disk
21
+ * persist across process restarts the same way `LocalRegistry` does.
22
+ *
23
+ * No auto-commit. Resources extracted via `resources.inject` land in the
24
+ * worktree as untracked files; whether to commit them is the agent's call,
25
+ * matching platform behavior.
26
+ */
27
+ import type { Logger } from '@roj-ai/sdk';
28
+ export declare class GitInstanceFs {
29
+ private readonly dataPath;
30
+ private readonly logger;
31
+ constructor(dataPath: string, logger: Logger);
32
+ instanceDir(instanceId: string): string;
33
+ repoDir(instanceId: string): string;
34
+ worktreeDir(instanceId: string, sessionId: string): string;
35
+ /**
36
+ * Idempotently initialize the bare repo for an instance with an empty
37
+ * initial commit on `main`. The commit exists so subsequent
38
+ * `git worktree add -b session/{sid}` calls have something to branch from.
39
+ */
40
+ initInstance(instanceId: string): Promise<void>;
41
+ /**
42
+ * Idempotently add a worktree for `sessionId` on branch `session/{sid}`,
43
+ * branching from `main`. Throws if the target dir exists but is not a valid
44
+ * worktree of this instance's bare repo (unrecoverable conflict).
45
+ */
46
+ addSessionWorktree(instanceId: string, sessionId: string): Promise<string>;
47
+ /**
48
+ * Remove a session's worktree (filesystem + bookkeeping). Silently no-ops
49
+ * if the worktree was never registered.
50
+ */
51
+ removeSessionWorktree(instanceId: string, sessionId: string): Promise<void>;
52
+ /**
53
+ * Tear down everything for an instance: every worktree, then the
54
+ * bare repo + on-disk dir. Safe to call when nothing exists.
55
+ */
56
+ removeInstance(instanceId: string): Promise<void>;
57
+ private isLinkedWorktree;
58
+ private git;
59
+ }
60
+ //# sourceMappingURL=git-instance-fs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-instance-fs.d.ts","sourceRoot":"","sources":["../src/git-instance-fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAmBzC,qBAAa,aAAa;IAExB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM;IAGhC,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAIvC,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAInC,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAI1D;;;;OAIG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBrD;;;;OAIG;IACG,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAyBhF;;;OAGG;IACG,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBjF;;;OAGG;IACG,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAOzC,gBAAgB;YAYhB,GAAG;CAiBjB"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Per-instance git layout for the standalone server.
3
+ *
4
+ * Mirrors what `roj-platform` does inside an E2B sandbox: each instance owns
5
+ * a bare repo, each session owns a worktree branched from `main`. The SDK
6
+ * receives the worktree path as `workspaceDir` on `sessions.create` and treats
7
+ * it as an opaque directory — it doesn't know about git.
8
+ *
9
+ * {dataPath}/instances/{instanceId}/repo.git/ ← bare, branch `main`
10
+ * {dataPath}/instances/{instanceId}/sessions/{sid}/ ← worktree, branch `session/{sid}`
11
+ *
12
+ * Why a bare repo + worktrees instead of a plain dir per session:
13
+ * - Parallel sessions on one instance need independent commit history.
14
+ * - `git worktree` shares object storage, so the cost is mostly metadata.
15
+ * - Mental model matches platform — agent `git status` / `git log` behave
16
+ * identically against standalone and against E2B.
17
+ *
18
+ * Idempotent: `initInstance` skips if `repo.git` already exists,
19
+ * `addSessionWorktree` skips if the target worktree is already registered.
20
+ * That keeps `roj-standalone` restart-safe — bare repos and worktrees on disk
21
+ * persist across process restarts the same way `LocalRegistry` does.
22
+ *
23
+ * No auto-commit. Resources extracted via `resources.inject` land in the
24
+ * worktree as untracked files; whether to commit them is the agent's call,
25
+ * matching platform behavior.
26
+ */
27
+ import { execFile } from 'node:child_process';
28
+ import { existsSync } from 'node:fs';
29
+ import { mkdir, rm } from 'node:fs/promises';
30
+ import { join } from 'node:path';
31
+ import { promisify } from 'node:util';
32
+ const execFileAsync = promisify(execFile);
33
+ // Used for the empty initial commit on `main`. Real commits the agent makes
34
+ // will pick up the host's git config; this just keeps `commit-tree` from
35
+ // failing on machines without `user.name`/`user.email` set.
36
+ const BOOTSTRAP_GIT_ENV = {
37
+ GIT_AUTHOR_NAME: 'roj-standalone',
38
+ GIT_AUTHOR_EMAIL: 'standalone@roj.local',
39
+ GIT_COMMITTER_NAME: 'roj-standalone',
40
+ GIT_COMMITTER_EMAIL: 'standalone@roj.local',
41
+ };
42
+ export class GitInstanceFs {
43
+ dataPath;
44
+ logger;
45
+ constructor(dataPath, logger) {
46
+ this.dataPath = dataPath;
47
+ this.logger = logger;
48
+ }
49
+ instanceDir(instanceId) {
50
+ return join(this.dataPath, 'instances', instanceId);
51
+ }
52
+ repoDir(instanceId) {
53
+ return join(this.instanceDir(instanceId), 'repo.git');
54
+ }
55
+ worktreeDir(instanceId, sessionId) {
56
+ return join(this.instanceDir(instanceId), 'sessions', sessionId);
57
+ }
58
+ /**
59
+ * Idempotently initialize the bare repo for an instance with an empty
60
+ * initial commit on `main`. The commit exists so subsequent
61
+ * `git worktree add -b session/{sid}` calls have something to branch from.
62
+ */
63
+ async initInstance(instanceId) {
64
+ const repoDir = this.repoDir(instanceId);
65
+ if (existsSync(repoDir)) {
66
+ this.logger.debug('Instance bare repo already exists, skipping init', { instanceId });
67
+ return;
68
+ }
69
+ await mkdir(this.instanceDir(instanceId), { recursive: true });
70
+ await this.git(['init', '--bare', repoDir]);
71
+ // `git init --bare` may default to `master` on older git; normalize so
72
+ // the worktree branch starts from a known parent ref.
73
+ await this.git(['symbolic-ref', 'HEAD', 'refs/heads/main'], { cwd: repoDir });
74
+ const tree = (await this.git(['mktree'], { cwd: repoDir, input: '' })).trim();
75
+ const commit = (await this.git(['commit-tree', tree, '-m', 'Initial commit'], { cwd: repoDir, env: BOOTSTRAP_GIT_ENV })).trim();
76
+ await this.git(['update-ref', 'refs/heads/main', commit], { cwd: repoDir });
77
+ this.logger.info('Initialized instance bare repo', { instanceId, repoDir });
78
+ }
79
+ /**
80
+ * Idempotently add a worktree for `sessionId` on branch `session/{sid}`,
81
+ * branching from `main`. Throws if the target dir exists but is not a valid
82
+ * worktree of this instance's bare repo (unrecoverable conflict).
83
+ */
84
+ async addSessionWorktree(instanceId, sessionId) {
85
+ const repoDir = this.repoDir(instanceId);
86
+ const worktreePath = this.worktreeDir(instanceId, sessionId);
87
+ if (!existsSync(repoDir)) {
88
+ throw new Error(`Cannot add session worktree: instance bare repo missing at ${repoDir}`);
89
+ }
90
+ if (existsSync(worktreePath)) {
91
+ if (await this.isLinkedWorktree(repoDir, worktreePath)) {
92
+ this.logger.debug('Session worktree already exists, skipping add', { instanceId, sessionId });
93
+ return worktreePath;
94
+ }
95
+ throw new Error(`Path exists but is not a registered worktree of ${repoDir}: ${worktreePath}`);
96
+ }
97
+ await mkdir(join(this.instanceDir(instanceId), 'sessions'), { recursive: true });
98
+ await this.git(['worktree', 'add', '-b', `session/${sessionId}`, worktreePath, 'main'], { cwd: repoDir });
99
+ this.logger.info('Added session worktree', { instanceId, sessionId, worktreePath });
100
+ return worktreePath;
101
+ }
102
+ /**
103
+ * Remove a session's worktree (filesystem + bookkeeping). Silently no-ops
104
+ * if the worktree was never registered.
105
+ */
106
+ async removeSessionWorktree(instanceId, sessionId) {
107
+ const repoDir = this.repoDir(instanceId);
108
+ const worktreePath = this.worktreeDir(instanceId, sessionId);
109
+ if (!existsSync(repoDir) || !existsSync(worktreePath))
110
+ return;
111
+ try {
112
+ await this.git(['worktree', 'remove', '--force', worktreePath], { cwd: repoDir });
113
+ }
114
+ catch (err) {
115
+ // worktree may already be detached; fall back to filesystem rm + prune.
116
+ this.logger.warn('git worktree remove failed, falling back to rm + prune', {
117
+ instanceId,
118
+ sessionId,
119
+ error: err instanceof Error ? err.message : String(err),
120
+ });
121
+ await rm(worktreePath, { recursive: true, force: true });
122
+ await this.git(['worktree', 'prune'], { cwd: repoDir }).catch(() => { });
123
+ }
124
+ }
125
+ /**
126
+ * Tear down everything for an instance: every worktree, then the
127
+ * bare repo + on-disk dir. Safe to call when nothing exists.
128
+ */
129
+ async removeInstance(instanceId) {
130
+ const dir = this.instanceDir(instanceId);
131
+ if (!existsSync(dir))
132
+ return;
133
+ await rm(dir, { recursive: true, force: true });
134
+ this.logger.info('Removed instance directory', { instanceId, dir });
135
+ }
136
+ async isLinkedWorktree(repoDir, worktreePath) {
137
+ try {
138
+ const stdout = await this.git(['worktree', 'list', '--porcelain'], { cwd: repoDir });
139
+ // `worktree` lines look like: `worktree /abs/path` (one per registration).
140
+ return stdout
141
+ .split('\n')
142
+ .some((line) => line.startsWith('worktree ') && line.slice('worktree '.length) === worktreePath);
143
+ }
144
+ catch {
145
+ return false;
146
+ }
147
+ }
148
+ async git(args, opts = {}) {
149
+ const env = opts.env ? { ...process.env, ...opts.env } : process.env;
150
+ const child = execFileAsync('git', args, { cwd: opts.cwd, env });
151
+ if (opts.input !== undefined) {
152
+ child.child.stdin?.end(opts.input);
153
+ }
154
+ try {
155
+ const { stdout } = await child;
156
+ return stdout;
157
+ }
158
+ catch (err) {
159
+ const message = err instanceof Error ? err.message : String(err);
160
+ throw new Error(`git ${args.join(' ')} failed: ${message}`);
161
+ }
162
+ }
163
+ }
164
+ //# sourceMappingURL=git-instance-fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-instance-fs.js","sourceRoot":"","sources":["../src/git-instance-fs.ts"],"names":[],"mappings":"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"}
@@ -4,16 +4,22 @@
4
4
  * A standalone server hosts exactly one "instance" — a workspace containing
5
5
  * sessions. The instance id is either supplied via config or auto-generated
6
6
  * on startup and remains stable for the process lifetime.
7
+ *
8
+ * `metadata` is mutated by `instances.create` calls (last write wins). It is
9
+ * a single-tenant local-dev convenience; consumers that need per-instance
10
+ * isolation must run separate processes.
7
11
  */
8
12
  export interface InstanceState {
9
13
  id: string;
10
14
  name: string;
11
15
  createdAt: string;
12
16
  presetIds: string[];
17
+ metadata: Record<string, unknown> | null;
13
18
  }
14
19
  export declare function createInstance(options: {
15
20
  id?: string;
16
21
  name?: string;
17
22
  presetIds: string[];
23
+ metadata?: Record<string, unknown> | null;
18
24
  }): InstanceState;
19
25
  //# sourceMappingURL=instance.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,aAAa,CAO1G"}
1
+ {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CACxC;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE;IACvC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CACzC,GAAG,aAAa,CAQhB"}
package/dist/instance.js CHANGED
@@ -4,6 +4,10 @@
4
4
  * A standalone server hosts exactly one "instance" — a workspace containing
5
5
  * sessions. The instance id is either supplied via config or auto-generated
6
6
  * on startup and remains stable for the process lifetime.
7
+ *
8
+ * `metadata` is mutated by `instances.create` calls (last write wins). It is
9
+ * a single-tenant local-dev convenience; consumers that need per-instance
10
+ * isolation must run separate processes.
7
11
  */
8
12
  export function createInstance(options) {
9
13
  return {
@@ -11,6 +15,7 @@ export function createInstance(options) {
11
15
  name: options.name ?? 'standalone',
12
16
  createdAt: new Date().toISOString(),
13
17
  presetIds: options.presetIds,
18
+ metadata: options.metadata ?? null,
14
19
  };
15
20
  }
16
21
  //# sourceMappingURL=instance.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"instance.js","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,MAAM,UAAU,cAAc,CAAC,OAA4D;IAC1F,OAAO;QACN,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,SAAS;QAC3B,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,YAAY;QAClC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,SAAS,EAAE,OAAO,CAAC,SAAS;KAC5B,CAAA;AACF,CAAC"}
1
+ {"version":3,"file":"instance.js","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH,MAAM,UAAU,cAAc,CAAC,OAK9B;IACA,OAAO;QACN,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,SAAS;QAC3B,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,YAAY;QAClC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,IAAI;KAClC,CAAA;AACF,CAAC"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * On-disk file + resource registry for the standalone server.
3
+ *
4
+ * Stands in for the platform's Contember-backed file/resource service. State
5
+ * lives in `{dataPath}/.roj-platform-state.json` (JSON manifest) plus
6
+ * `{dataPath}/.roj-platform-state/files/{fileId}` (raw bytes). Operations are
7
+ * append-only at the resource level (revisions accumulate), so re-running
8
+ * `roj-standalone` between sessions preserves uploads and revisions added
9
+ * during a previous run.
10
+ *
11
+ * Files are content-addressable by SHA-256 — re-uploading the same bytes
12
+ * returns the existing fileId with `deduped: true`, mirroring the platform's
13
+ * /files/upload contract used by `@roj-ai/client/platform.files.upload`.
14
+ */
15
+ import type { LocalResource, Logger } from '@roj-ai/sdk';
16
+ interface FileMeta {
17
+ id: string;
18
+ filename: string;
19
+ mimeType: string;
20
+ size: number;
21
+ contentHash: string;
22
+ createdAt: string;
23
+ }
24
+ export interface ResourceOutput {
25
+ id: string;
26
+ slug: string;
27
+ name: string | null;
28
+ description: string | null;
29
+ latestRevision: {
30
+ id: string;
31
+ label: string | null;
32
+ file: {
33
+ id: string;
34
+ filename: string;
35
+ mimeType: string;
36
+ size: number;
37
+ };
38
+ createdAt: string;
39
+ } | null;
40
+ createdAt: string;
41
+ }
42
+ export interface UploadResult {
43
+ fileId: string;
44
+ filename: string;
45
+ mimeType: string;
46
+ size: number;
47
+ deduped: boolean;
48
+ }
49
+ export declare class LocalRegistry {
50
+ private readonly dataPath;
51
+ private readonly logger;
52
+ private state;
53
+ private statePath;
54
+ private filesDir;
55
+ constructor(dataPath: string, logger: Logger);
56
+ init(): Promise<void>;
57
+ uploadFile(args: {
58
+ buffer?: Buffer;
59
+ filename: string;
60
+ mimeType: string;
61
+ contentHash?: string;
62
+ }): Promise<UploadResult | {
63
+ error: 'file-required';
64
+ contentHash: string;
65
+ }>;
66
+ readFileById(fileId: string): Promise<{
67
+ meta: FileMeta;
68
+ buffer: Buffer;
69
+ } | null>;
70
+ createResource(args: {
71
+ slug: string;
72
+ name?: string;
73
+ description?: string;
74
+ fileId: string;
75
+ label?: string;
76
+ }): Promise<{
77
+ resourceId: string;
78
+ revisionId: string;
79
+ }>;
80
+ addRevision(args: {
81
+ resourceId?: string;
82
+ resourceSlug?: string;
83
+ fileId: string;
84
+ label?: string;
85
+ }): Promise<{
86
+ revisionId: string;
87
+ noop: boolean;
88
+ }>;
89
+ getResource(args: {
90
+ resourceId?: string;
91
+ resourceSlug?: string;
92
+ }): ResourceOutput | null;
93
+ listResources(): ResourceOutput[];
94
+ deleteResource(resourceId: string): Promise<{
95
+ ok: boolean;
96
+ }>;
97
+ /**
98
+ * Idempotent: ensures every config-declared local resource exists in the
99
+ * registry. Skips slugs that already have a record (so previously uploaded
100
+ * revisions survive restarts).
101
+ */
102
+ bootstrapLocalResources(localResources: LocalResource[]): Promise<void>;
103
+ private persist;
104
+ private findResource;
105
+ private findBySlug;
106
+ }
107
+ export declare function clearLocalRegistry(dataPath: string): Promise<void>;
108
+ export {};
109
+ //# sourceMappingURL=local-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-registry.d.ts","sourceRoot":"","sources":["../src/local-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAExD,UAAU,QAAQ;IACjB,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;CACjB;AAuBD,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,cAAc,EAAE;QACf,EAAE,EAAE,MAAM,CAAA;QACV,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;QACpB,IAAI,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;QACtE,SAAS,EAAE,MAAM,CAAA;KACjB,GAAG,IAAI,CAAA;IACR,SAAS,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;CAChB;AAED,qBAAa,aAAa;IAMxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IANxB,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,QAAQ,CAAQ;gBAGN,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM;IAM1B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAerB,UAAU,CAAC,IAAI,EAAE;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,QAAQ,EAAE,MAAM,CAAA;QAChB,QAAQ,EAAE,MAAM,CAAA;QAChB,WAAW,CAAC,EAAE,MAAM,CAAA;KACpB,GAAG,OAAO,CAAC,YAAY,GAAG;QAAE,KAAK,EAAE,eAAe,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAqDrE,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAWhF,cAAc,CAAC,IAAI,EAAE;QAC1B,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,MAAM,EAAE,MAAM,CAAA;QACd,KAAK,CAAC,EAAE,MAAM,CAAA;KACd,GAAG,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAuBjD,WAAW,CAAC,IAAI,EAAE;QACvB,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,MAAM,EAAE,MAAM,CAAA;QACd,KAAK,CAAC,EAAE,MAAM,CAAA;KACd,GAAG,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;IAwBlD,WAAW,CAAC,IAAI,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,cAAc,GAAG,IAAI;IAKxF,aAAa,IAAI,cAAc,EAAE;IAI3B,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC;IAOlE;;;;OAIG;IACG,uBAAuB,CAAC,cAAc,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAsB/D,OAAO;IAMrB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,UAAU;CAMlB;AAuCD,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKxE"}