@astrosheep/square 0.3.63 → 0.3.64
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/claude-plugin/.claude-plugin/plugin.json +3 -2
- package/claude-plugin/hooks/hooks.json +1 -22
- package/claude-plugin/hooks/register.js +119 -0
- package/claude-plugin/types.d.ts +7 -0
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/dist/activity-feed.d.ts +1 -10
- package/dist/activity-feed.js +1 -27
- package/dist/activity.js +57 -25
- package/dist/artifact.js +12 -24
- package/dist/attention-presentation.js +1 -0
- package/dist/automatic-session.d.ts +3 -1
- package/dist/automatic-session.js +21 -34
- package/dist/boundary-presentation.js +4 -13
- package/dist/canonical-path.d.ts +11 -0
- package/dist/canonical-path.js +47 -0
- package/dist/catch-decisions.js +14 -10
- package/dist/catch-selection.d.ts +6 -0
- package/dist/catch-selection.js +14 -0
- package/dist/claude-delivery.d.ts +11 -0
- package/dist/claude-delivery.js +145 -0
- package/dist/claude-mod.d.ts +8 -0
- package/dist/claude-mod.js +164 -0
- package/dist/cli/context.d.ts +0 -1
- package/dist/cli/context.js +9 -21
- package/dist/cli/harness-command.d.ts +0 -1
- package/dist/cli/harness-command.js +0 -6
- package/dist/cli/observation-commands.d.ts +1 -1
- package/dist/cli/observation-commands.js +31 -58
- package/dist/cli/program.js +20 -18
- package/dist/cli/registry.js +2 -2
- package/dist/cli/square-commands.js +10 -6
- package/dist/codex-hook.js +9 -12
- package/dist/codex-queue.d.ts +2 -2
- package/dist/codex-queue.js +7 -5
- package/dist/decisions.d.ts +2 -6
- package/dist/decisions.js +16 -17
- package/dist/delivery-health.d.ts +1 -2
- package/dist/delivery-health.js +4 -5
- package/dist/delivery-operations.d.ts +2 -5
- package/dist/delivery-operations.js +77 -214
- package/dist/delivery.d.ts +25 -47
- package/dist/delivery.js +14 -46
- package/dist/diagnostic-redaction.js +7 -9
- package/dist/file-changes.d.ts +0 -1
- package/dist/file-changes.js +2 -19
- package/dist/harness-claude.js +8 -6
- package/dist/harness-codex.d.ts +0 -2
- package/dist/harness-codex.js +0 -2
- package/dist/harness-links.js +13 -17
- package/dist/harness.js +3 -7
- package/dist/help.js +4 -3
- package/dist/host-ledger-file-adapter.d.ts +17 -8
- package/dist/host-ledger-file-adapter.js +164 -80
- package/dist/host-ledger.d.ts +55 -50
- package/dist/inbox.d.ts +0 -3
- package/dist/inbox.js +13 -30
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/list.js +1 -4
- package/dist/mcp.js +18 -40
- package/dist/mention-parse.js +3 -9
- package/dist/model.d.ts +11 -2
- package/dist/model.js +1 -1
- package/dist/notifications.d.ts +3 -16
- package/dist/notifications.js +41 -122
- package/dist/opencode-delivery.d.ts +14 -0
- package/dist/opencode-delivery.js +171 -0
- package/dist/opencode.d.ts +39 -18
- package/dist/opencode.js +145 -40
- package/dist/packages/agent-delivery/src/claude-auth.d.ts +13 -0
- package/dist/packages/agent-delivery/src/claude-auth.js +70 -0
- package/dist/packages/agent-delivery/src/claude-native.d.ts +28 -0
- package/dist/packages/agent-delivery/src/claude-native.js +65 -0
- package/dist/packages/agent-delivery/src/claude.d.ts +14 -0
- package/dist/packages/agent-delivery/src/claude.js +66 -0
- package/dist/packages/agent-delivery/src/deadline.d.ts +11 -0
- package/dist/packages/agent-delivery/src/deadline.js +41 -0
- package/dist/packages/agent-delivery/src/errors.d.ts +16 -0
- package/dist/packages/agent-delivery/src/errors.js +41 -0
- package/dist/packages/agent-delivery/src/guards.d.ts +6 -0
- package/dist/packages/agent-delivery/src/guards.js +6 -0
- package/dist/packages/agent-delivery/src/index.d.ts +83 -0
- package/dist/packages/agent-delivery/src/index.js +110 -0
- package/dist/packages/agent-delivery/src/local-endpoint.d.ts +26 -0
- package/dist/packages/agent-delivery/src/local-endpoint.js +114 -0
- package/dist/packages/agent-delivery/src/opencode-native.d.ts +33 -0
- package/dist/packages/agent-delivery/src/opencode-native.js +75 -0
- package/dist/packages/agent-delivery/src/opencode.d.ts +14 -0
- package/dist/packages/agent-delivery/src/opencode.js +127 -0
- package/dist/packages/agent-delivery/src/paseo-native.d.ts +58 -0
- package/dist/packages/agent-delivery/src/paseo-native.js +122 -0
- package/dist/packages/agent-delivery/src/paseo.d.ts +19 -0
- package/dist/packages/agent-delivery/src/paseo.js +106 -0
- package/dist/packages/agent-delivery/src/pi-client.d.ts +10 -0
- package/dist/packages/agent-delivery/src/pi-client.js +106 -0
- package/dist/packages/agent-delivery/src/pi-protocol.d.ts +15 -0
- package/dist/packages/agent-delivery/src/pi-protocol.js +50 -0
- package/dist/packages/agent-delivery/src/pi-send.d.ts +23 -0
- package/dist/packages/agent-delivery/src/pi-send.js +4 -0
- package/dist/packages/agent-delivery/src/pi.d.ts +25 -0
- package/dist/packages/agent-delivery/src/pi.js +209 -0
- package/dist/packages/agent-delivery/src/types.d.ts +120 -0
- package/dist/participant-host.js +12 -13
- package/dist/participant-identity.d.ts +21 -0
- package/dist/participant-identity.js +32 -11
- package/dist/paseo-delivery.d.ts +2 -2
- package/dist/paseo-delivery.js +35 -27
- package/dist/paseo-timeline.js +25 -21
- package/dist/paseo.d.ts +2 -2
- package/dist/paseo.js +1 -2
- package/dist/ports.d.ts +7 -20
- package/dist/presentation-operations.js +16 -4
- package/dist/presentation.d.ts +19 -10
- package/dist/presentation.js +99 -177
- package/dist/registry.d.ts +15 -66
- package/dist/registry.js +45 -123
- package/dist/routes.d.ts +1 -28
- package/dist/routes.js +22 -104
- package/dist/runtime.d.ts +3 -9
- package/dist/runtime.js +14 -67
- package/dist/search.d.ts +0 -2
- package/dist/search.js +3 -10
- package/dist/square-actions.js +48 -72
- package/dist/square-application.js +25 -23
- package/dist/square-core.d.ts +3 -0
- package/dist/square-core.js +22 -5
- package/dist/square-facade.d.ts +2 -0
- package/dist/square-file-adapter.js +4 -7
- package/dist/square-projections.d.ts +40 -45
- package/dist/square-projections.js +43 -73
- package/dist/square-state.js +1 -4
- package/dist/square-storage.d.ts +5 -8
- package/dist/square-storage.js +13 -36
- package/dist/square-wiring.d.ts +0 -5
- package/dist/square-wiring.js +3 -21
- package/dist/stream.js +4 -6
- package/dist/time.js +2 -7
- package/dist/views.d.ts +4 -16
- package/dist/views.js +12 -19
- package/dist/wake-attempts.d.ts +2 -56
- package/dist/wake-attempts.js +15 -110
- package/dist/wake-eligibility.d.ts +43 -0
- package/dist/wake-eligibility.js +106 -0
- package/dist/wake-evidence.d.ts +32 -9
- package/dist/wake-evidence.js +29 -24
- package/dist/wake-sink.d.ts +13 -9
- package/dist/wake-sink.js +35 -54
- package/dist/watch.js +13 -21
- package/extensions/square-pi.js +10 -5
- package/guides/claude-native.md +101 -0
- package/guides/host-ledger.md +3 -1
- package/guides/participant.md +14 -7
- package/guides/pi-native-delivery.md +43 -0
- package/package.json +4 -3
- package/dist/claude-hook.d.ts +0 -10
- package/dist/claude-hook.js +0 -60
- package/dist/paseo-connection.d.ts +0 -15
- package/dist/paseo-connection.js +0 -141
- package/dist/presence.d.ts +0 -2
- package/dist/presence.js +0 -24
- package/dist/presented.d.ts +0 -2
- package/dist/presented.js +0 -10
- package/dist/state-cell.d.ts +0 -14
- package/dist/wake-port.d.ts +0 -23
- package/dist/wake-port.js +0 -27
- /package/dist/{state-cell.js → packages/agent-delivery/src/types.js} +0 -0
package/dist/paseo-connection.js
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import { homedir } from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { DaemonClient } from '@getpaseo/client/internal/daemon-client';
|
|
5
|
-
import WebSocket from 'ws';
|
|
6
|
-
const DEFAULT_HOST = 'localhost:6767';
|
|
7
|
-
const DEFAULT_CONNECT_TIMEOUT_MS = 3_000;
|
|
8
|
-
function paseoHome(env) {
|
|
9
|
-
return env.PASEO_HOME?.trim() || path.join(homedir(), '.paseo');
|
|
10
|
-
}
|
|
11
|
-
function expandHome(value) {
|
|
12
|
-
return value === '~' ? homedir() : value.startsWith('~/') ? path.join(homedir(), value.slice(2)) : value;
|
|
13
|
-
}
|
|
14
|
-
function normalizeHost(raw) {
|
|
15
|
-
if (typeof raw !== 'string' || raw.trim() === '')
|
|
16
|
-
return undefined;
|
|
17
|
-
const value = raw.trim();
|
|
18
|
-
if (value.startsWith('unix://') || value.startsWith('pipe://') || value.startsWith('tcp://'))
|
|
19
|
-
return value;
|
|
20
|
-
if (value.startsWith('\\\\.\\pipe\\'))
|
|
21
|
-
return `pipe://${value}`;
|
|
22
|
-
if (value.startsWith('/') || value.startsWith('~/'))
|
|
23
|
-
return `unix://${expandHome(value)}`;
|
|
24
|
-
if (/^\d+$/.test(value))
|
|
25
|
-
return `127.0.0.1:${value}`;
|
|
26
|
-
return value.includes(':') ? value : undefined;
|
|
27
|
-
}
|
|
28
|
-
function configuredHost(env) {
|
|
29
|
-
try {
|
|
30
|
-
const config = JSON.parse(fs.readFileSync(path.join(paseoHome(env), 'config.json'), 'utf8'));
|
|
31
|
-
return normalizeHost(config.daemon?.listen ?? config.listen);
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
return undefined;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function pidHost(env) {
|
|
38
|
-
try {
|
|
39
|
-
const pid = JSON.parse(fs.readFileSync(path.join(paseoHome(env), 'paseo.pid'), 'utf8'));
|
|
40
|
-
return normalizeHost(pid.listen ?? pid.sockPath);
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
return undefined;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function isIpc(host) {
|
|
47
|
-
return host !== undefined && (host.startsWith('unix://') || host.startsWith('pipe://'));
|
|
48
|
-
}
|
|
49
|
-
function supportsIpcHost(host) {
|
|
50
|
-
return process.platform !== 'win32' || !host.startsWith('unix://');
|
|
51
|
-
}
|
|
52
|
-
/** Match the host precedence used by the Paseo CLI for local daemon connections. */
|
|
53
|
-
export function paseoDaemonHosts(env = process.env) {
|
|
54
|
-
const explicit = normalizeHost(env.PASEO_HOST);
|
|
55
|
-
if (explicit !== undefined)
|
|
56
|
-
return [explicit];
|
|
57
|
-
const candidates = [];
|
|
58
|
-
const listen = normalizeHost(env.PASEO_LISTEN);
|
|
59
|
-
const pid = pidHost(env);
|
|
60
|
-
const configured = configuredHost(env);
|
|
61
|
-
if (isIpc(listen) && supportsIpcHost(listen))
|
|
62
|
-
candidates.push(listen);
|
|
63
|
-
if (isIpc(pid) && supportsIpcHost(pid))
|
|
64
|
-
candidates.push(pid);
|
|
65
|
-
if (isIpc(configured) && supportsIpcHost(configured))
|
|
66
|
-
candidates.push(configured);
|
|
67
|
-
if (configured !== undefined && !isIpc(configured) && configured !== '127.0.0.1:6767')
|
|
68
|
-
candidates.push(configured);
|
|
69
|
-
candidates.push(DEFAULT_HOST);
|
|
70
|
-
return [...new Set(candidates)];
|
|
71
|
-
}
|
|
72
|
-
function uriPassword(uri) {
|
|
73
|
-
const value = uri.searchParams.get('password');
|
|
74
|
-
return value === null || value === '' ? undefined : value;
|
|
75
|
-
}
|
|
76
|
-
export function resolvePaseoDaemonTarget(host, env = process.env) {
|
|
77
|
-
const passwordFromEnv = env.PASEO_PASSWORD?.trim() || undefined;
|
|
78
|
-
if (host.startsWith('unix://') || host.startsWith('pipe://')) {
|
|
79
|
-
if (process.platform === 'win32' && host.startsWith('unix://')) {
|
|
80
|
-
throw new Error('Paseo Unix socket targets are unsupported on Windows; use pipe:// or tcp://.');
|
|
81
|
-
}
|
|
82
|
-
const prefix = host.startsWith('unix://') ? 'unix://' : 'pipe://';
|
|
83
|
-
const socketPath = expandHome(host.slice(prefix.length).trim());
|
|
84
|
-
if (socketPath === '')
|
|
85
|
-
throw new Error('Invalid Paseo IPC target: missing socket path.');
|
|
86
|
-
return {
|
|
87
|
-
type: 'ipc',
|
|
88
|
-
url: host.startsWith('unix://') ? `ws+unix://${socketPath}:/ws` : 'ws://localhost/ws',
|
|
89
|
-
socketPath,
|
|
90
|
-
...(passwordFromEnv === undefined ? {} : { password: passwordFromEnv }),
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
if (host.startsWith('tcp://')) {
|
|
94
|
-
const uri = new URL(host);
|
|
95
|
-
const hostname = uri.hostname.replace(/^\[|\]$/g, '');
|
|
96
|
-
const endpoint = `${hostname.includes(':') ? `[${hostname}]` : hostname}:${uri.port || '6767'}`;
|
|
97
|
-
const secure = uri.searchParams.get('ssl') === 'true';
|
|
98
|
-
const password = uriPassword(uri) ?? passwordFromEnv;
|
|
99
|
-
return {
|
|
100
|
-
type: 'tcp',
|
|
101
|
-
url: `${secure ? 'wss' : 'ws'}://${endpoint}/ws`,
|
|
102
|
-
...(password === undefined ? {} : { password }),
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
return {
|
|
106
|
-
type: 'tcp',
|
|
107
|
-
url: `ws://${host.replace(/\/$/, '')}/ws`,
|
|
108
|
-
...(passwordFromEnv === undefined ? {} : { password: passwordFromEnv }),
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
function webSocketFactory(target) {
|
|
112
|
-
return (url, options) => new WebSocket(url, options?.protocols, {
|
|
113
|
-
headers: options?.headers,
|
|
114
|
-
...(target.type === 'ipc' ? { socketPath: target.socketPath } : {}),
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
export async function connectPaseoDaemon(env = process.env, connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS) {
|
|
118
|
-
let lastError;
|
|
119
|
-
for (const host of paseoDaemonHosts(env)) {
|
|
120
|
-
const target = resolvePaseoDaemonTarget(host, env);
|
|
121
|
-
const client = new DaemonClient({
|
|
122
|
-
url: target.url,
|
|
123
|
-
clientId: `square-${process.pid}-${Date.now()}`,
|
|
124
|
-
clientType: 'cli',
|
|
125
|
-
appVersion: 'square',
|
|
126
|
-
password: target.password,
|
|
127
|
-
connectTimeoutMs,
|
|
128
|
-
webSocketFactory: webSocketFactory(target),
|
|
129
|
-
reconnect: { enabled: false },
|
|
130
|
-
});
|
|
131
|
-
try {
|
|
132
|
-
await client.connect();
|
|
133
|
-
return client;
|
|
134
|
-
}
|
|
135
|
-
catch (error) {
|
|
136
|
-
lastError = error;
|
|
137
|
-
await client.close().catch(() => { });
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
throw lastError instanceof Error ? lastError : new Error('Unable to connect to the Paseo daemon.');
|
|
141
|
-
}
|
package/dist/presence.d.ts
DELETED
package/dist/presence.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { openSquare } from './square-file-adapter.js';
|
|
2
|
-
import { closeOpenSquare } from './open-square.js';
|
|
3
|
-
import { recordObservation } from './runtime.js';
|
|
4
|
-
/** Commit seen only for complete, actually rendered boundary bodies. */
|
|
5
|
-
export async function markBoundarySeen(squarePath, name, actIndexes, at = Date.now()) {
|
|
6
|
-
let square;
|
|
7
|
-
try {
|
|
8
|
-
square = await openSquare(squarePath);
|
|
9
|
-
}
|
|
10
|
-
catch {
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
try {
|
|
14
|
-
await square.artifact.transact((state) => {
|
|
15
|
-
let changed = false;
|
|
16
|
-
for (const index of actIndexes)
|
|
17
|
-
changed = recordObservation(state, name, index, 'seen', at) || changed;
|
|
18
|
-
return changed ? { state, result: undefined } : { result: undefined };
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
finally {
|
|
22
|
-
await closeOpenSquare(square);
|
|
23
|
-
}
|
|
24
|
-
}
|
package/dist/presented.d.ts
DELETED
package/dist/presented.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import os from 'node:os';
|
|
2
|
-
import { hostLedgerRoot } from './host-ledger-root.js';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { canonicalSquarePath } from './registry.js';
|
|
5
|
-
import { createHostLedgerPort } from './host-ledger-file-adapter.js';
|
|
6
|
-
import { formatActivityId } from './square-core.js';
|
|
7
|
-
import { projectPresentationEvidence } from './square-projections.js';
|
|
8
|
-
export function presentedPath(env = process.env) { return env.SQUARE_PRESENTED || path.join(os.homedir(), '.square', 'presented.ndjsonl'); }
|
|
9
|
-
function evidence(env = process.env) { const file = presentedPath(env); return createHostLedgerPort({ rootPath: hostLedgerRoot(env) }); }
|
|
10
|
-
export async function hasPresentedForOwner(sessionId, squarePath, name, actIndex, env = process.env, now = Date.now()) { const resolved = await canonicalSquarePath(squarePath); return (await projectPresentationEvidence({ hostLedger: evidence(env), now, location: resolved, participant: name, sessionId, activity: formatActivityId(actIndex) })).some((row) => row.outcome === 'presented'); }
|
package/dist/state-cell.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { SquareState } from './model.js';
|
|
2
|
-
/** Synchronous state transition seam shared by memory and file storage. */
|
|
3
|
-
export interface StateCell {
|
|
4
|
-
transact<R>(fn: (state: SquareState, version: number) => {
|
|
5
|
-
state?: SquareState;
|
|
6
|
-
result: R;
|
|
7
|
-
}, signal?: AbortSignal): Promise<R>;
|
|
8
|
-
read(signal?: AbortSignal): Promise<{
|
|
9
|
-
state: SquareState;
|
|
10
|
-
version: number;
|
|
11
|
-
}>;
|
|
12
|
-
changed(sinceVersion: number, timeoutMs: number, signal?: AbortSignal): Promise<boolean>;
|
|
13
|
-
close(): Promise<void>;
|
|
14
|
-
}
|
package/dist/wake-port.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { WakeAdapter, WakeDispatchResult } from './delivery.js';
|
|
2
|
-
import type { WakeRoute } from './model.js';
|
|
3
|
-
export interface WakePortHooks {
|
|
4
|
-
nextAttemptN(): number | Promise<number>;
|
|
5
|
-
beforeSend(route: WakeRoute, attemptN: number): Promise<boolean>;
|
|
6
|
-
record(route: WakeRoute, attemptN: number, result: Exclude<WakeDispatchResult, {
|
|
7
|
-
outcome: 'cancelled' | 'unavailable';
|
|
8
|
-
}>): Promise<void>;
|
|
9
|
-
invalidate?(route: WakeRoute, result: Extract<WakeDispatchResult, {
|
|
10
|
-
outcome: 'unavailable';
|
|
11
|
-
}>): Promise<void>;
|
|
12
|
-
}
|
|
13
|
-
export type WakePortResult = {
|
|
14
|
-
outcome: 'accepted' | 'unknown' | 'cancelled';
|
|
15
|
-
} | {
|
|
16
|
-
outcome: 'exhausted';
|
|
17
|
-
};
|
|
18
|
-
/** Select routes globally; adapters own only transport-specific live proof and dispatch. */
|
|
19
|
-
export declare class WakePort {
|
|
20
|
-
private readonly adapters;
|
|
21
|
-
constructor(adapters: WakeAdapter[]);
|
|
22
|
-
dispatch(routes: readonly WakeRoute[], payload: string | ((route: WakeRoute) => string), hooks: WakePortHooks): Promise<WakePortResult>;
|
|
23
|
-
}
|
package/dist/wake-port.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/** Select routes globally; adapters own only transport-specific live proof and dispatch. */
|
|
2
|
-
export class WakePort {
|
|
3
|
-
adapters;
|
|
4
|
-
constructor(adapters) {
|
|
5
|
-
this.adapters = new Map(adapters.map((adapter) => [adapter.kind, adapter]));
|
|
6
|
-
}
|
|
7
|
-
async dispatch(routes, payload, hooks) {
|
|
8
|
-
for (const route of routes) {
|
|
9
|
-
const adapter = this.adapters.get(route.kind);
|
|
10
|
-
if (adapter === undefined)
|
|
11
|
-
continue;
|
|
12
|
-
const attemptN = await hooks.nextAttemptN();
|
|
13
|
-
const result = await adapter.dispatch(route.address, typeof payload === 'function' ? payload(route) : payload, () => hooks.beforeSend(route, attemptN));
|
|
14
|
-
if (result.outcome === 'cancelled')
|
|
15
|
-
return result;
|
|
16
|
-
if (result.outcome === 'unavailable') {
|
|
17
|
-
if (result.routeStale === true)
|
|
18
|
-
await hooks.invalidate?.(route, result);
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
await hooks.record(route, attemptN, result);
|
|
22
|
-
if (result.outcome === 'accepted' || result.outcome === 'unknown')
|
|
23
|
-
return { outcome: result.outcome };
|
|
24
|
-
}
|
|
25
|
-
return { outcome: 'exhausted' };
|
|
26
|
-
}
|
|
27
|
-
}
|
|
File without changes
|